am 8337fa4e: am c8596a95: am 157dae46: am 88051ed3: Merge "Add testInterfaceProperties() . Move Ethernet verification out of testLoopback()." into kitkat-cts-dev automerge: 19679b0 automerge: 2cc1870
* commit '8337fa4e58ee38757a707d2d4829abe761f89a78':
Add testInterfaceProperties() . Move Ethernet verification out of testLoopback().
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index 9f6d827..fb0b207 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -116,16 +116,6 @@
LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
include $(BUILD_JAVA_LIBRARY)
-# Path to the ICU4C data files in the Android device file system:
-icu4c_data := /system/usr/icu
-# TODO: It's quite hideous that this double-slash between icu4j and main is required.
-# It's because we provide a variable substition of the make-rule generated jar command
-# to substitute a processed ICUProperties.config file in place of the original.
-#
-# We can avoid this by filtering out ICUConfig.properties from our list of resources.
-icu4j_config_root := $(LOCAL_PATH)/../external/icu/icu4j//main/classes/core/src
-include external/icu/icu4j/adjust_icudt_path.mk
-
ifeq ($(LIBCORE_SKIP_TESTS),)
# Make the core-tests library.
include $(CLEAR_VARS)
diff --git a/NativeCode.mk b/NativeCode.mk
index b21dd2d..379a1cb 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -117,6 +117,7 @@
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE := libjavacore-unit-tests
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+LOCAL_SHARED_LIBRARIES := libnativehelper
LOCAL_CXX_STL := libc++
include $(BUILD_NATIVE_TEST)
@@ -131,6 +132,7 @@
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE := libjavacore-benchmarks
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+LOCAL_SHARED_LIBRARIES := libnativehelper
LOCAL_CXX_STL := libc++
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
@@ -175,6 +177,7 @@
LOCAL_MODULE := libjavacoretests
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
LOCAL_SHARED_LIBRARIES := libcrypto-host
+ LOCAL_MULTILIB := both
LOCAL_CXX_STL := libc++
include $(BUILD_HOST_SHARED_LIBRARY)
endif # LIBCORE_SKIP_TESTS
diff --git a/benchmarks/src/benchmarks/ClassLoaderResourceBenchmark.java b/benchmarks/src/benchmarks/ClassLoaderResourceBenchmark.java
new file mode 100644
index 0000000..379eaec
--- /dev/null
+++ b/benchmarks/src/benchmarks/ClassLoaderResourceBenchmark.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * 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 benchmarks;
+
+import com.google.caliper.SimpleBenchmark;
+
+import junit.framework.Assert;
+
+public class ClassLoaderResourceBenchmark extends SimpleBenchmark {
+
+ private static final String EXISTENT_RESOURCE = "java/util/logging/logging.properties";
+ private static final String MISSING_RESOURCE = "missing_entry";
+
+ public void timeGetBootResource_hit(int reps) {
+ ClassLoader currentClassLoader = getClass().getClassLoader();
+ Assert.assertNotNull(currentClassLoader.getResource(EXISTENT_RESOURCE));
+
+ for (int rep = 0; rep < reps; ++rep) {
+ currentClassLoader.getResource(EXISTENT_RESOURCE);
+ }
+ }
+
+ public void timeGetBootResource_miss(int reps) {
+ ClassLoader currentClassLoader = getClass().getClassLoader();
+ Assert.assertNull(currentClassLoader.getResource(MISSING_RESOURCE));
+
+ for (int rep = 0; rep < reps; ++rep) {
+ currentClassLoader.getResource(MISSING_RESOURCE);
+ }
+ }
+
+}
diff --git a/benchmarks/src/benchmarks/XmlSerializeBenchmark.java b/benchmarks/src/benchmarks/XmlSerializeBenchmark.java
new file mode 100644
index 0000000..7319490
--- /dev/null
+++ b/benchmarks/src/benchmarks/XmlSerializeBenchmark.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * 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 benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import org.xmlpull.v1.*;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Random;
+
+
+public class XmlSerializeBenchmark extends SimpleBenchmark {
+
+ @Param( {"0.99 0.7 0.7 0.7 0.7 0.7",
+ "0.999 0.3 0.3 0.95 0.9 0.9"})
+ String datasetAsString;
+
+ @Param( { "854328", "312547"} )
+ int seed;
+
+ double[] dataset;
+ private Constructor<? extends XmlSerializer> kxmlConstructor;
+ private Constructor<? extends XmlSerializer> fastConstructor;
+
+ private void serializeRandomXml(Constructor<? extends XmlSerializer> ctor, long seed)
+ throws Exception {
+ double contChance = dataset[0];
+ double levelUpChance = dataset[1];
+ double levelDownChance = dataset[2];
+ double attributeChance = dataset[3];
+ double writeChance1 = dataset[4];
+ double writeChance2 = dataset[5];
+
+ XmlSerializer serializer = (XmlSerializer) ctor.newInstance();
+
+ CharArrayWriter w = new CharArrayWriter();
+ serializer.setOutput(w);
+ int level = 0;
+ Random r = new Random(seed);
+ char[] toWrite = {'a','b','c','d','s','z'};
+ serializer.startDocument("UTF-8", true);
+ while(r.nextDouble() < contChance) {
+ while(level > 0 && r.nextDouble() < levelUpChance) {
+ serializer.endTag("aaaaaa", "bbbbbb");
+ level--;
+ }
+ while(r.nextDouble() < levelDownChance) {
+ serializer.startTag("aaaaaa", "bbbbbb");
+ level++;
+ }
+ serializer.startTag("aaaaaa", "bbbbbb");
+ level++;
+ while(r.nextDouble() < attributeChance) {
+ serializer.attribute("aaaaaa", "cccccc", "dddddd");
+ }
+ serializer.endTag("aaaaaa", "bbbbbb");
+ level--;
+ while(r.nextDouble() < writeChance1)
+ serializer.text(toWrite, 0, 5);
+ while(r.nextDouble() < writeChance2)
+ serializer.text("Textxtsxtxtxt ");
+ }
+ serializer.endDocument();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void setUp() throws Exception {
+ kxmlConstructor = (Constructor) Class.forName("org.kxml2.io.KXmlSerializer")
+ .getConstructor();
+ fastConstructor = (Constructor) Class.forName("com.android.internal.util.FastXmlSerializer")
+ .getConstructor();
+ String[] splitted = datasetAsString.split(" ");
+ dataset = new double[splitted.length];
+ for (int i = 0; i < splitted.length; i++) {
+ dataset[i] = Double.valueOf(splitted[i]);
+ }
+ }
+
+ private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor, int reps)
+ throws Exception {
+ for (int i = 0; i < reps; i++) {
+ serializeRandomXml(ctor, seed);
+ }
+ }
+
+ public void timeKxml(int reps) throws Exception {
+ internalTimeSerializer(kxmlConstructor, reps);
+ }
+
+ public void timeFast(int reps) throws Exception {
+ internalTimeSerializer(fastConstructor, reps);
+ }
+
+ public static void main(String[] args) {
+ Runner.main(XmlSerializeBenchmark.class, args);
+ }
+}
diff --git a/benchmarks/src/benchmarks/regression/StrictMathBenchmark.java b/benchmarks/src/benchmarks/regression/StrictMathBenchmark.java
index 44c030a..e844d93 100644
--- a/benchmarks/src/benchmarks/regression/StrictMathBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/StrictMathBenchmark.java
@@ -30,6 +30,36 @@
private final int i = 1;
private final long l = 1L;
+ /* Values for full line coverage of ceiling function */
+ private static final double[] CEIL_DOUBLES = new double[] {
+ 3245817.2018463886,
+ 1418139.083668501,
+ 3.572936802189103E15,
+ -4.7828929737254625E249,
+ 213596.58636369856,
+ 6.891928421440976E-96,
+ -7.9318566885477E-36,
+ -1.9610339084804148E15,
+ -4.696725715628246E10,
+ 3742491.296880909,
+ 7.140274745333553E11
+ };
+
+ /* Values for full line coverage of floor function */
+ private static final double[] FLOOR_DOUBLES = new double[] {
+ 7.140274745333553E11,
+ 3742491.296880909,
+ -4.696725715628246E10,
+ -1.9610339084804148E15,
+ 7.049948629370372E-56,
+ -7.702933170334643E-16,
+ -1.99657681810579,
+ -1.1659287182288336E236,
+ 4.085518816513057E15,
+ -1500948.440658056,
+ -2.2316479921415575E7
+ };
+
public void timeAbsD(int reps) {
for (int rep = 0; rep < reps; ++rep) {
StrictMath.abs(d);
@@ -84,9 +114,11 @@
}
}
- public void timeCeil(int reps) {
+ public void timeCeilOverInterestingValues(int reps) {
for (int rep = 0; rep < reps; ++rep) {
- StrictMath.ceil(d);
+ for (int i = 0; i < CEIL_DOUBLES.length; ++i) {
+ StrictMath.ceil(CEIL_DOUBLES[i]);
+ }
}
}
@@ -126,9 +158,11 @@
}
}
- public void timeFloor(int reps) {
+ public void timeFloorOverInterestingValues(int reps) {
for (int rep = 0; rep < reps; ++rep) {
- StrictMath.floor(d);
+ for (int i = 0; i < FLOOR_DOUBLES.length; ++i) {
+ StrictMath.floor(FLOOR_DOUBLES[i]);
+ }
}
}
diff --git a/benchmarks/src/benchmarks/regression/StringEqualsBenchmark.java b/benchmarks/src/benchmarks/regression/StringEqualsBenchmark.java
new file mode 100644
index 0000000..3ba2ac1
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/StringEqualsBenchmark.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * 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 benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import junit.framework.Assert;
+
+/**
+ * Benchmarks to measure the performance of String.equals for Strings of varying lengths.
+ * Each benchmarks makes 5 measurements, aiming at covering cases like strings of equal length
+ * that are not equal, identical strings with different references, strings with different endings,
+ * interned strings, and strings of different lengths.
+ */
+public class StringEqualsBenchmark extends SimpleBenchmark {
+ private final String long1 = "Ahead-of-time compilation is possible as the compiler may just"
+ + "convert an instruction thus: dex code: add-int v1000, v2000, v3000 C code: setIntRegter"
+ + "(1000, call_dex_add_int(getIntRegister(2000), getIntRegister(3000)) This means even lid"
+ + "instructions may have code generated, however, it is not expected that code generate in"
+ + "this way will perform well. The job of AOT verification is to tell the compiler that"
+ + "instructions are sound and provide tests to detect unsound sequences so slow path code"
+ + "may be generated. Other than for totally invalid code, the verification may fail at AOr"
+ + "run-time. At AOT time it can be because of incomplete information, at run-time it can e"
+ + "that code in a different apk that the application depends upon has changed. The Dalvik"
+ + "verifier would return a bool to state whether a Class were good or bad. In ART the fail"
+ + "case becomes either a soft or hard failure. Classes have new states to represent that a"
+ + "soft failure occurred at compile time and should be re-verified at run-time.";
+
+ private final String veryLong = "Garbage collection has two phases. The first distinguishes"
+ + "live objects from garbage objects. The second is reclaiming the rage of garbage object"
+ + "In the mark-sweep algorithm used by Dalvik, the first phase is achievd by computing the"
+ + "closure of all reachable objects in a process known as tracing from theoots. After the"
+ + "trace has completed, garbage objects are reclaimed. Each of these operations can be"
+ + "parallelized and can be interleaved with the operation of the applicationTraditionally,"
+ + "the tracing phase dominates the time spent in garbage collection. The greatreduction i"
+ + "pause time can be achieved by interleaving as much of this phase as possible with the"
+ + "application. If we simply ran the GC in a separate thread with no other changes, normal"
+ + "operation of an application would confound the trace. Abstractly, the GC walks the h o"
+ + "all reachable objects. When the application is paused, the object graph cannot change."
+ + "The GC can therefore walk this structure and assume that all reachable objects live."
+ + "When the application is running, this graph may be altered. New nodes may be addnd edge"
+ + "may be changed. These changes may cause live objects to be hidden and falsely recla by"
+ + "the GC. To avoid this problem a write barrier is used to intercept and record modifion"
+ + "to objects in a separate structure. After performing its walk, the GC will revisit the"
+ + "updated objects and re-validate its assumptions. Without a card table, the garbage"
+ + "collector would have to visit all objects reached during the trace looking for dirtied"
+ + "objects. The cost of this operation would be proportional to the amount of live data."
+ + "With a card table, the cost of this operation is proportional to the amount of updateat"
+ + "The write barrier in Dalvik is a card marking write barrier. Card marking is the proce"
+ + "of noting the location of object connectivity changes on a sub-page granularity. A car"
+ + "is merely a colorful term for a contiguous extent of memory smaller than a page, common"
+ + "somewhere between 128- and 512-bytes. Card marking is implemented by instrumenting all"
+ + "locations in the virtual machine which can assign a pointer to an object. After themal"
+ + "pointer assignment has occurred, a byte is written to a byte-map spanning the heap whic"
+ + "corresponds to the location of the updated object. This byte map is known as a card ta"
+ + "The garbage collector visits this card table and looks for written bytes to reckon the"
+ + "location of updated objects. It then rescans all objects located on the dirty card,"
+ + "correcting liveness assumptions that were invalidated by the application. While card"
+ + "marking imposes a small burden on the application outside of a garbage collection, the"
+ + "overhead of maintaining the card table is paid for by the reduced time spent inside"
+ + "garbage collection. With the concurrent garbage collection thread and a write barrier"
+ + "supported by the interpreter, JIT, and Runtime we modify garbage collection";
+
+ private final String[][] shortStrings = new String[][] {
+ // Equal, constant comparison
+ { "a", "a" },
+ // Different constants, first character different
+ { ":", " :"},
+ // Different constants, last character different, same length
+ { "ja M", "ja N"},
+ // Different constants, different lengths
+ {"$$$", "$$"},
+ // Force execution of code beyond reference equality check
+ {"hi", new String("hi")}
+ };
+
+ private final String[][] mediumStrings = new String[][] {
+ // Equal, constant comparison
+ { "Hello my name is ", "Hello my name is " },
+ // Different constants, different lengths
+ { "What's your name?", "Whats your name?" },
+ // Force execution of code beyond reference equality check
+ { "Android Runtime", new String("Android Runtime") },
+ // Different constants, last character different, same length
+ { "v3ry Cre@tiVe?****", "v3ry Cre@tiVe?***." },
+ // Different constants, first character different, same length
+ { "!@#$%^&*()_++*^$#@", "0@#$%^&*()_++*^$#@" }
+ };
+
+ private final String[][] longStrings = new String[][] {
+ // Force execution of code beyond reference equality check
+ { long1, new String(long1) },
+ // Different constants, last character different, same length
+ { long1 + "fun!", long1 + "----" },
+ // Equal, constant comparison
+ { long1 + long1, long1 + long1 },
+ // Different constants, different lengths
+ { long1 + "123456789", long1 + "12345678" },
+ // Different constants, first character different, same length
+ { "Android Runtime" + long1, "android Runtime" + long1 }
+ };
+
+ private final String[][] veryLongStrings = new String[][] {
+ // Force execution of code beyond reference equality check
+ { veryLong, new String(veryLong) },
+ // Different constants, different lengths
+ { veryLong + veryLong, veryLong + " " + veryLong },
+ // Equal, constant comparison
+ { veryLong + veryLong + veryLong, veryLong + veryLong + veryLong },
+ // Different constants, last character different, same length
+ { veryLong + "77777", veryLong + "99999" },
+ // Different constants, first character different
+ { "Android Runtime" + veryLong, "android Runtime" + veryLong }
+ };
+
+ private final String[][] endStrings = new String[][] {
+ // Different constants, medium but different lengths
+ { "Hello", "Hello " },
+ // Different constants, long but different lengths
+ { long1, long1 + "x"},
+ // Different constants, very long but different lengths
+ { veryLong, veryLong + "?"},
+ // Different constants, same medium lengths
+ { "How are you doing today?", "How are you doing today " },
+ // Different constants, short but different lengths
+ { "1", "1." }
+ };
+
+ private final String tmpStr1 = "012345678901234567890"
+ + "0123456789012345678901234567890123456789"
+ + "0123456789012345678901234567890123456789"
+ + "0123456789012345678901234567890123456789"
+ + "0123456789012345678901234567890123456789";
+
+ private final String tmpStr2 = "z012345678901234567890"
+ + "0123456789012345678901234567890123456789"
+ + "0123456789012345678901234567890123456789"
+ + "0123456789012345678901234567890123456789"
+ + "012345678901234567890123456789012345678x";
+
+ private final String[][] nonalignedStrings = new String[][] {
+ // Different non-word aligned medium length strings
+ { tmpStr1, tmpStr1.substring(1) },
+ // Different differently non-word aligned medium length strings
+ { tmpStr2, tmpStr2.substring(2) },
+ // Different non-word aligned long length strings
+ { long1, long1.substring(3) },
+ // Different non-word aligned very long length strings
+ { veryLong, veryLong.substring(1) },
+ // Equal non-word aligned constant strings
+ { "hello", "hello".substring(1) }
+ };
+
+ private final Object[] objects = new Object[] {
+ // Compare to Double object
+ new Double(1.5),
+ // Compare to Integer object
+ new Integer(9999999),
+ // Compare to String array
+ new String[] {"h", "i"},
+ // Compare to int array
+ new int[] {1, 2, 3},
+ // Compare to Character object
+ new Character('a')
+ };
+
+ // Check assumptions about how the compiler, new String(String), and String.intern() work.
+ // Any failures here would invalidate these benchmarks.
+ @Override protected void setUp() throws Exception {
+ // String constants are the same object
+ Assert.assertSame("abc", "abc");
+ // new String(String) makes a copy
+ Assert.assertNotSame("abc" , new String("abc"));
+ // Interned strings are treated like constants, so it is not necessary to
+ // separately benchmark interned strings.
+ Assert.assertSame("abc", "abc".intern());
+ Assert.assertSame("abc", new String("abc").intern());
+ // Compiler folds constant strings into new constants
+ Assert.assertSame(long1 + long1, long1 + long1);
+ }
+
+ // Benchmark cases of String.equals(null)
+ public void timeEqualsNull(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < mediumStrings.length; i++) {
+ mediumStrings[i][0].equals(null);
+ }
+ }
+ }
+
+ // Benchmark cases with very short (<5 character) Strings
+ public void timeEqualsShort(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < shortStrings.length; i++) {
+ shortStrings[i][0].equals(shortStrings[i][1]);
+ }
+ }
+ }
+
+ // Benchmark cases with medium length (10-15 character) Strings
+ public void timeEqualsMedium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < mediumStrings.length; i++) {
+ mediumStrings[i][0].equals(mediumStrings[i][1]);
+ }
+ }
+ }
+
+ // Benchmark cases with long (>100 character) Strings
+ public void timeEqualsLong(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < longStrings.length; i++) {
+ longStrings[i][0].equals(longStrings[i][1]);
+ }
+ }
+ }
+
+ // Benchmark cases with very long (>1000 character) Strings
+ public void timeEqualsVeryLong(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < veryLongStrings.length; i++) {
+ veryLongStrings[i][0].equals(veryLongStrings[i][1]);
+ }
+ }
+ }
+
+ // Benchmark cases with non-word aligned Strings
+ public void timeEqualsNonWordAligned(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < nonalignedStrings.length; i++) {
+ nonalignedStrings[i][0].equals(nonalignedStrings[i][1]);
+ }
+ }
+ }
+
+ // Benchmark cases with slight differences in the endings
+ public void timeEqualsEnd(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < endStrings.length; i++) {
+ endStrings[i][0].equals(endStrings[i][1]);
+ }
+ }
+ }
+
+ // Benchmark cases of comparing a string to a non-string object
+ public void timeEqualsNonString(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < mediumStrings.length; i++) {
+ mediumStrings[i][0].equals(objects[i]);
+ }
+ }
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index e25feb9..5552b1c 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -27,11 +27,12 @@
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
-import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import static android.system.OsConstants.*;
+import libcore.io.ClassPathURLStreamHandler;
+
+import static android.system.OsConstants.S_ISDIR;
/**
* A pair of lists of entries, associated with a {@code ClassLoader}.
@@ -117,8 +118,8 @@
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
// save dexPath for BaseDexClassLoader
- this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory,
- suppressedExceptions);
+ this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
+ suppressedExceptions);
// Native libraries may exist in both the system and
// application library paths, and we use this search order:
@@ -136,7 +137,7 @@
List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
- this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null,
+ this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories,
suppressedExceptions);
if (suppressedExceptions.size() > 0) {
@@ -169,8 +170,7 @@
/**
* Splits the given dex path string into elements using the path
* separator, pruning out any elements that do not refer to existing
- * and readable files. (That is, directories are not included in the
- * result.)
+ * and readable files.
*/
private static List<File> splitDexPath(String path) {
return splitPaths(path, false);
@@ -211,8 +211,31 @@
* Makes an array of dex/resource path elements, one per element of
* the given array.
*/
+ private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
+ List<IOException> suppressedExceptions) {
+ return makeElements(files, optimizedDirectory, suppressedExceptions, false);
+ }
+
+ /**
+ * Makes an array of directory/zip path elements, one per element of the given array.
+ */
+ private static Element[] makePathElements(List<File> files,
+ List<IOException> suppressedExceptions) {
+ return makeElements(files, null, suppressedExceptions, true);
+ }
+
+ /*
+ * TODO (dimitry): Revert after GMS core stops relying on the existence of this
+ * method (see b/21957414 for details)
+ */
private static Element[] makePathElements(List<File> files, File optimizedDirectory,
List<IOException> suppressedExceptions) {
+ return makeElements(files, null, suppressedExceptions, true);
+ }
+
+ private static Element[] makeElements(List<File> files, File optimizedDirectory,
+ List<IOException> suppressedExceptions,
+ boolean ignoreDexFiles) {
List<Element> elements = new ArrayList<>();
/*
* Open all files and load the (direct or contained) dex files
@@ -234,7 +257,7 @@
// Looking up resources in directories is useful for running libcore tests.
elements.add(new Element(file, true, null, null));
} else if (file.isFile()) {
- if (name.endsWith(DEX_SUFFIX)) {
+ if (!ignoreDexFiles && name.endsWith(DEX_SUFFIX)) {
// Raw dex file (not inside a zip/jar).
try {
dex = loadDexFile(file, optimizedDirectory);
@@ -244,17 +267,19 @@
} else {
zip = file;
- try {
- dex = loadDexFile(file, optimizedDirectory);
- } catch (IOException suppressed) {
- /*
- * IOException might get thrown "legitimately" by the DexFile constructor if
- * the zip file turns out to be resource-only (that is, no classes.dex file
- * in it).
- * Let dex == null and hang on to the exception to add to the tea-leaves for
- * when findClass returns null.
- */
- suppressedExceptions.add(suppressed);
+ if (!ignoreDexFiles) {
+ try {
+ dex = loadDexFile(file, optimizedDirectory);
+ } catch (IOException suppressed) {
+ /*
+ * IOException might get thrown "legitimately" by the DexFile constructor if
+ * the zip file turns out to be resource-only (that is, no classes.dex file
+ * in it).
+ * Let dex == null and hang on to the exception to add to the tea-leaves for
+ * when findClass returns null.
+ */
+ suppressedExceptions.add(suppressed);
+ }
}
}
} else {
@@ -408,7 +433,7 @@
}
/**
- * Element of the dex/resource file path
+ * Element of the dex/resource/native library path
*/
/*package*/ static class Element {
private final File dir;
@@ -416,7 +441,7 @@
private final File zip;
private final DexFile dexFile;
- private ZipFile zipFile;
+ private ClassPathURLStreamHandler urlHandler;
private boolean initialized;
public Element(File dir, boolean isDirectory, File zip, DexFile dexFile) {
@@ -449,7 +474,7 @@
}
try {
- zipFile = new ZipFile(zip);
+ urlHandler = new ClassPathURLStreamHandler(zip.getPath());
} catch (IOException ioe) {
/*
* Note: ZipException (a subclass of IOException)
@@ -458,25 +483,10 @@
* file).
*/
System.logE("Unable to open zip file: " + zip, ioe);
- zipFile = null;
+ urlHandler = null;
}
}
- /**
- * Returns true if entry with specified path exists and not compressed.
- *
- * Note that ZipEntry does not provide information about offset so we
- * cannot reliably check if entry is page-aligned. For now we are going
- * take optimistic approach and rely on (1) if library was extracted
- * it would have been found by the previous step (2) if library was not extracted
- * but STORED and not page-aligned the installation of the app would have failed
- * because of checks in PackageManagerService.
- */
- private boolean isZipEntryExistsAndStored(ZipFile zipFile, String path) {
- ZipEntry entry = zipFile.getEntry(path);
- return entry != null && entry.getMethod() == ZipEntry.STORED;
- }
-
public String findNativeLibrary(String name) {
maybeInit();
@@ -485,9 +495,13 @@
if (IoUtils.canOpenReadOnly(path)) {
return path;
}
- } else if (zipFile != null) {
+ } else if (urlHandler != null) {
+ // Having a urlHandler means the element has a zip file.
+ // In this case Android supports loading the library iff
+ // it is stored in the zip uncompressed.
+
String entryName = new File(dir, name).getPath();
- if (isZipEntryExistsAndStored(zipFile, entryName)) {
+ if (urlHandler.isEntryStored(entryName)) {
return zip.getPath() + zipSeparator + entryName;
}
}
@@ -511,27 +525,12 @@
}
}
- if (zipFile == null || zipFile.getEntry(name) == null) {
- /*
- * Either this element has no zip/jar file (first
- * clause), or the zip/jar file doesn't have an entry
- * for the given name (second clause).
+ if (urlHandler == null) {
+ /* This element has no zip/jar file.
*/
return null;
}
-
- try {
- /*
- * File.toURL() is compliant with RFC 1738 in
- * always creating absolute path names. If we
- * construct the URL by concatenating strings, we
- * might end up with illegal URLs for relative
- * names.
- */
- return new URL("jar:" + zip.toURL() + "!/" + name);
- } catch (MalformedURLException ex) {
- throw new RuntimeException(ex);
- }
+ return urlHandler.getEntryUrlOrNull(name);
}
}
}
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
index 59e28e2..23d7407 100644
--- a/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -361,6 +361,8 @@
/**
* Counts the instances of a class.
+ * It is the caller's responsibility to do GC if they don't want unreachable
+ * objects to get counted.
*
* @param klass the class to be counted.
* @param assignable if false, direct instances of klass are
@@ -372,6 +374,21 @@
public static native long countInstancesOfClass(Class klass, boolean assignable);
/**
+ * Counts the instances of classes.
+ * It is the caller's responsibility to do GC if they don't want unreachable
+ * objects to get counted.
+ *
+ * @param classes the classes to be counted.
+ * @param assignable if false, direct instances of klass are
+ * counted. If true, instances that are
+ * assignable to klass, as defined by
+ * {@link Class#isAssignableFrom} are counted.
+ * @return an array containing the number of matching instances. The value for
+ * an index is the number of instances of the class at that index in number classes.
+ */
+ public static native long[] countInstancesOfClasses(Class[] classes, boolean assignable);
+
+ /**
* Export the heap per-space stats for dumpsys meminfo.
*
* The content of the array is:
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java
index d9f3d91..e8f8e29 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java
@@ -819,7 +819,7 @@
}
public void test_getReuseAddress() throws Exception {
- DatagramSocket theSocket = new DatagramSocket();
+ DatagramSocket theSocket = new DatagramSocket(null);
theSocket.setReuseAddress(true);
assertTrue("getReuseAddress false when it should be true", theSocket.getReuseAddress());
theSocket.setReuseAddress(false);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SinkChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SinkChannelTest.java
index 32f59c5..55a8fc3 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SinkChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SinkChannelTest.java
@@ -463,7 +463,8 @@
public void test_socketChannel_read_close() throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(),49999));
+ ssc.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), 0 /* any free port */));
+ int localPort = ssc.socket().getLocalPort();
SocketChannel sc = SocketChannel.open();
ByteBuffer buf = null;
try{
@@ -472,7 +473,7 @@
}catch (NullPointerException e){
// expected
}
- sc.connect(new InetSocketAddress(InetAddress.getLocalHost(),49999));
+ sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), localPort));
SocketChannel sock = ssc.accept();
ssc.close();
sc.close();
@@ -487,9 +488,10 @@
public void test_socketChannel_read_write() throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(),49999));
+ ssc.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), 0 /* any free port */));
+ int localPort = ssc.socket().getLocalPort();
SocketChannel sc = SocketChannel.open();
- sc.connect(new InetSocketAddress(InetAddress.getLocalHost(),49999));
+ sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), localPort));
SocketChannel sock = ssc.accept();
ByteBuffer[] buf = {ByteBuffer.allocate(10),null};
try {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java
index 277abce..8b4844c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArraysTest.java
@@ -2217,7 +2217,7 @@
long[] b = new long[a.length];
for (int i = 0; i < a.length; i++) {
- b[i] = (int) a[i];
+ b[i] = (long) a[i];
}
return b;
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java
index c0e814e..87e177f 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java
@@ -152,6 +152,8 @@
private TimeZone defaultTimeZone;
+ private Locale defaultLocale;
+
/**
* java.util.Formatter#Formatter()
*/
@@ -4164,6 +4166,7 @@
* test the short name for timezone whether uses DaylightTime or not
*/
public void test_DaylightTime() {
+ Locale.setDefault(Locale.US);
Calendar c1 = new GregorianCalendar(2007, 0, 1);
Calendar c2 = new GregorianCalendar(2007, 7, 1);
@@ -4216,6 +4219,8 @@
secret = File.createTempFile("secret", null);
+ defaultLocale = Locale.getDefault();
+
defaultTimeZone = TimeZone.getDefault();
TimeZone cst = TimeZone.getTimeZone("Asia/Shanghai");
TimeZone.setDefault(cst);
@@ -4239,6 +4244,7 @@
secret.delete();
}
+ Locale.setDefault(defaultLocale);
TimeZone.setDefault(defaultTimeZone);
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/GregorianCalendarTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/GregorianCalendarTest.java
index f35be4b..6f25495 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/GregorianCalendarTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/GregorianCalendarTest.java
@@ -31,6 +31,20 @@
private static final TimeZone AMERICA_CHICAGO = TimeZone.getTimeZone("America/Chicago");
private static final TimeZone AMERICA_NEW_YORK = TimeZone.getTimeZone("America/New_York");
+ private Locale defaultLocale;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ defaultLocale = Locale.getDefault();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ super.tearDown();
+ }
+
/**
* java.util.GregorianCalendar#GregorianCalendar()
*/
@@ -531,6 +545,8 @@
* java.util.GregorianCalendar#roll(int, boolean)
*/
public void test_rollIZ() {
+ Locale.setDefault(Locale.US);
+
// Test for method void java.util.GregorianCalendar.roll(int, boolean)
GregorianCalendar gc = new GregorianCalendar(1972, Calendar.OCTOBER,
13, 19, 9, 59);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/RandomTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/RandomTest.java
index d51d8abf..f790a31 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/RandomTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/RandomTest.java
@@ -18,12 +18,13 @@
package org.apache.harmony.tests.java.util;
import java.io.Serializable;
+import java.util.Arrays;
import java.util.Random;
import org.apache.harmony.testframework.serialization.SerializationTest;
public class RandomTest extends junit.framework.TestCase {
- Random r;
+ private Random r;
/**
* java.util.Random#Random()
@@ -44,6 +45,17 @@
.nextInt() == r2.nextInt());
}
+ public void test_setSeed() {
+ Random r = new Random();
+ r.setSeed(1337);
+ Random r2 = new Random();
+ r2.setSeed(1337);
+ for (int i = 0; i < 100; i++)
+ assertTrue("Values from randoms with same seed don't match", r
+ .nextInt() == r2.nextInt());
+ }
+
+
/**
* java.util.Random#nextBoolean()
*/
@@ -216,38 +228,39 @@
*/
public void test_setSeedJ() {
// Test for method void java.util.Random.setSeed(long)
- long[] randomArray = new long[100];
- boolean someDifferent = false;
- final long firstSeed = 1000;
- long aLong, anotherLong, yetAnotherLong;
- Random aRandom = new Random();
- Random anotherRandom = new Random();
- Random yetAnotherRandom = new Random();
- aRandom.setSeed(firstSeed);
- anotherRandom.setSeed(firstSeed);
- for (int counter = 0; counter < randomArray.length; counter++) {
- aLong = aRandom.nextLong();
- anotherLong = anotherRandom.nextLong();
- assertTrue(
- "Two randoms with same seeds gave differing nextLong values",
- aLong == anotherLong);
- yetAnotherLong = yetAnotherRandom.nextLong();
- randomArray[counter] = aLong;
- if (aLong != yetAnotherLong)
- someDifferent = true;
+ long[] random1Values = new long[100];
+ long[] random2Values = new long[100];
+ long[] random3Values = new long[100];
+
+ Random random1 = new Random();
+ Random random2 = new Random();
+ Random random3 = new Random();
+
+ random1.setSeed(1337);
+ random2.setSeed(1337);
+ random3.setSeed(5000);
+
+ for (int i = 0; i < 100; ++i) {
+ random1Values[i] = random1.nextLong();
+ random2Values[i] = random2.nextLong();
+ random3Values[i] = random3.nextLong();
}
- assertTrue(
- "Two randoms with the different seeds gave the same chain of values",
- someDifferent);
- aRandom.setSeed(firstSeed);
- for (int counter = 0; counter < randomArray.length; counter++)
- assertTrue(
- "Reseting a random to its old seed did not result in the same chain of values as it gave before",
- aRandom.nextLong() == randomArray[counter]);
+
+ assertTrue(Arrays.equals(random1Values, random2Values));
+ assertFalse(Arrays.equals(random2Values, random3Values));
+
+ // Set random3's seed to 1337 and assert it results in the same sequence of
+ // values as the first two randoms.
+ random3.setSeed(1337);
+ for (int i = 0; i < 100; ++i) {
+ random3Values[i] = random3.nextLong();
+ }
+
+ assertTrue(Arrays.equals(random1Values, random3Values));
}
- class Mock_Random extends Random {
- boolean nextCalled = false;
+ static final class Mock_Random extends Random {
+ private boolean nextCalled = false;
public boolean getFlag () {
boolean retVal = nextCalled;
@@ -283,18 +296,12 @@
assertTrue(mr.getFlag());
}
- /**
- * Sets up the fixture, for example, open a network connection. This method
- * is called before a test is executed.
- */
+ @Override
protected void setUp() {
r = new Random();
}
- /**
- * Tears down the fixture, for example, close a network connection. This
- * method is called after a test is executed.
- */
+ @Override
protected void tearDown() {
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/x500/X500PrincipalTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/x500/X500PrincipalTest.java
index 34011c7..34978bc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/x500/X500PrincipalTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/auth/x500/X500PrincipalTest.java
@@ -1679,12 +1679,10 @@
* compares with expected value of name - "\nB"
*/
public void testNameSpecialChars_RFC1779_01() throws Exception {
- //FIXME see testNameSpecialChars_RFC2253_01
- // String dn = "CN=\\\nB";
- // X500Principal principal = new X500Principal(dn);
- // String s = principal.getName(X500Principal.RFC1779);
- // assertEquals("CN=\"\nB\"", s);
-
+ String dn = "CN=\\\nB";
+ X500Principal principal = new X500Principal(dn);
+ String s = principal.getName(X500Principal.RFC1779);
+ assertEquals("CN=\"\nB\"", s);
}
/**
@@ -1693,14 +1691,8 @@
* compares with expected value of name - \\nB
*/
public void testNameSpecialChars_RFC2253_01() throws Exception {
-
- try {
- // compatibility issue:
- // don't accept escaped \n because it is not a special char
- new X500Principal("CN=\\\nB");
- fail("No expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- }
+ X500Principal p = new X500Principal("CN=\\\nB");
+ assertEquals("CN=\nB", p.getName(X500Principal.RFC2253));
}
/**
@@ -2208,7 +2200,15 @@
String dn = "CN=A\nB";
X500Principal principal = new X500Principal(dn);
String s = principal.getName(X500Principal.RFC1779);
- assertEquals("CN=A\nB", s);
+ assertEquals("CN=\"A\nB\"", s);
+ }
+
+
+ public void testNamePlus_RFC1779() throws Exception {
+ String dn = "CN=A\\+B";
+ X500Principal principal = new X500Principal(dn);
+ String s = principal.getName(X500Principal.RFC1779);
+ assertEquals("CN=\"A+B\"", s);
}
/**
diff --git a/libart/src/main/java/java/lang/Class.java b/libart/src/main/java/java/lang/Class.java
index 388f34c..bc54f89 100644
--- a/libart/src/main/java/java/lang/Class.java
+++ b/libart/src/main/java/java/lang/Class.java
@@ -159,7 +159,14 @@
/** Lazily computed name of this class; always prefer calling getName(). */
private transient String name;
- /** The superclass, or null if this is java.lang.Object, an interface or primitive type. */
+ /**
+ * The superclass, or null if this is java.lang.Object or a primitive type.
+ *
+ * Note that interfaces have java.lang.Object as their
+ * superclass. This doesn't match the expectations of
+ * getSuperClass() which needs to check for interfaces and return
+ * null.
+ */
private transient Class<? super T> superClass;
/** If class verify fails, we must return same error on subsequent tries. */
diff --git a/libart/src/main/java/java/lang/VMClassLoader.java b/libart/src/main/java/java/lang/VMClassLoader.java
index d180a4d..a9d6253 100644
--- a/libart/src/main/java/java/lang/VMClassLoader.java
+++ b/libart/src/main/java/java/lang/VMClassLoader.java
@@ -16,33 +16,58 @@
package java.lang;
-import java.net.MalformedURLException;
+import java.io.File;
+import java.io.IOException;
import java.net.URL;
+import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.List;
+import libcore.io.ClassPathURLStreamHandler;
class VMClassLoader {
+ private static final ClassPathURLStreamHandler[] bootClassPathUrlHandlers;
+ static {
+ bootClassPathUrlHandlers = createBootClassPathUrlHandlers();
+ }
+
+ /**
+ * Creates an array of ClassPathURLStreamHandler objects for handling resource loading from
+ * the boot classpath.
+ */
+ private static ClassPathURLStreamHandler[] createBootClassPathUrlHandlers() {
+ String[] bootClassPathEntries = getBootClassPathEntries();
+ ArrayList<String> zipFileUris = new ArrayList<String>(bootClassPathEntries.length);
+ ArrayList<URLStreamHandler> urlStreamHandlers =
+ new ArrayList<URLStreamHandler>(bootClassPathEntries.length);
+ for (String bootClassPathEntry : bootClassPathEntries) {
+ try {
+ String entryUri = new File(bootClassPathEntry).toURI().toString();
+
+ // We assume all entries are zip or jar files.
+ URLStreamHandler urlStreamHandler =
+ new ClassPathURLStreamHandler(bootClassPathEntry);
+ zipFileUris.add(entryUri);
+ urlStreamHandlers.add(urlStreamHandler);
+ } catch (IOException e) {
+ // Skip it
+ System.logE("Unable to open boot classpath entry: " + bootClassPathEntry, e);
+ }
+ }
+ return urlStreamHandlers.toArray(new ClassPathURLStreamHandler[urlStreamHandlers.size()]);
+ }
+
/**
* Get a resource from a file in the bootstrap class path.
*
- * It would be simpler to just walk through the class path elements
- * ourselves, but that would require reopening Jar files.
- *
- * We assume that the bootclasspath can't change once the VM has
- * started. This assumption seems to be supported by the spec.
+ * We assume that the bootclasspath can't change once the VM has started.
+ * This assumption seems to be supported by the spec.
*/
static URL getResource(String name) {
- int numEntries = getBootClassPathSize();
- for (int i = 0; i < numEntries; i++) {
- String urlStr = getBootClassPathResource(name, i);
- if (urlStr != null) {
- try {
- return new URL(urlStr);
- } catch (MalformedURLException mue) {
- mue.printStackTrace();
- // unexpected; keep going
- }
+ for (ClassPathURLStreamHandler urlHandler : bootClassPathUrlHandlers) {
+ URL url = urlHandler.getEntryUrlOrNull(name);
+ if (url != null) {
+ return url;
}
}
return null;
@@ -53,16 +78,10 @@
*/
static List<URL> getResources(String name) {
ArrayList<URL> list = new ArrayList<URL>();
- int numEntries = getBootClassPathSize();
- for (int i = 0; i < numEntries; i++) {
- String urlStr = getBootClassPathResource(name, i);
- if (urlStr != null) {
- try {
- list.add(new URL(urlStr));
- } catch (MalformedURLException mue) {
- mue.printStackTrace();
- // unexpected; keep going
- }
+ for (ClassPathURLStreamHandler urlHandler : bootClassPathUrlHandlers) {
+ URL url = urlHandler.getEntryUrlOrNull(name);
+ if (url != null) {
+ list.add(url);
}
}
return list;
@@ -73,6 +92,6 @@
/**
* Boot class path manipulation, for getResources().
*/
- native private static int getBootClassPathSize();
- native private static String getBootClassPathResource(String name, int index);
+ native private static String[] getBootClassPathEntries();
+
}
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index a1e87c8..c43c1ba 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -43,6 +43,12 @@
public static FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
/**
+ * TODO Change the public API by removing the overload above and unhiding this version.
+ * @hide
+ */
+ public static FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
+
+ /**
* See <a href="http://man7.org/linux/man-pages/man2/access.2.html">access(2)</a>.
*/
public static boolean access(String path, int mode) throws ErrnoException { return Libcore.os.access(path, mode); }
@@ -540,6 +546,11 @@
public static StructUtsname uname() { return Libcore.os.uname(); }
/**
+ * @hide See <a href="http://man7.org/linux/man-pages/man2/unlink.2.html">unlink(2)</a>.
+ */
+ public static void unlink(String pathname) throws ErrnoException { Libcore.os.unlink(pathname); }
+
+ /**
* See <a href="http://man7.org/linux/man-pages/man3/unsetenv.3.html">unsetenv(3)</a>.
*/
public static void unsetenv(String name) throws ErrnoException { Libcore.os.unsetenv(name); }
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index 8832f3d..0908b88 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -238,6 +238,7 @@
public static final int ETIME = placeholder();
public static final int ETIMEDOUT = placeholder();
public static final int ETXTBSY = placeholder();
+ /** @hide */ public static final int EUSERS = placeholder();
// On Linux, EWOULDBLOCK == EAGAIN. Use EAGAIN instead, to reduce confusion.
public static final int EXDEV = placeholder();
public static final int EXIT_FAILURE = placeholder();
@@ -489,6 +490,8 @@
public static final int S_IXOTH = placeholder();
public static final int S_IXUSR = placeholder();
public static final int TCP_NODELAY = placeholder();
+ /** @hide */ public static final int TIOCOUTQ = placeholder();
+ /** @hide */ public static final int UNIX_PATH_MAX = placeholder();
public static final int WCONTINUED = placeholder();
public static final int WEXITED = placeholder();
public static final int WNOHANG = placeholder();
diff --git a/luni/src/main/java/android/system/UnixSocketAddress.java b/luni/src/main/java/android/system/UnixSocketAddress.java
new file mode 100644
index 0000000..e0ade78
--- /dev/null
+++ b/luni/src/main/java/android/system/UnixSocketAddress.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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 android.system;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * A UNIX-domain (AF_UNIX / AF_LOCAL) socket address.
+ *
+ * @hide
+ */
+public final class UnixSocketAddress extends SocketAddress {
+
+ private static final int NAMED_PATH_LENGTH = OsConstants.UNIX_PATH_MAX;
+ private static final byte[] UNNAMED_PATH = new byte[0];
+
+ // See unix(7): Three types of UnixSocketAddress:
+ // 1) pathname: 0 < sun_path.length <= NAMED_PATH_LENGTH, sun_path[0] != 0.
+ // 2) unnamed: sun_path = [].
+ // 3) abstract: 0 < sun_path.length <= NAMED_PATH_LENGTH, sun_path[0] == 0.
+ private byte[] sun_path;
+
+ /** This constructor is also used from JNI. */
+ private UnixSocketAddress(byte[] sun_path) {
+ if (sun_path == null) {
+ throw new IllegalArgumentException("sun_path must not be null");
+ }
+ if (sun_path.length > NAMED_PATH_LENGTH) {
+ throw new IllegalArgumentException("sun_path exceeds the maximum length");
+ }
+
+ if (sun_path.length == 0) {
+ this.sun_path = UNNAMED_PATH;
+ } else {
+ this.sun_path = new byte[sun_path.length];
+ System.arraycopy(sun_path, 0, this.sun_path, 0, sun_path.length);
+ }
+ }
+
+ /**
+ * Creates a named, abstract AF_UNIX socket address.
+ */
+ public static UnixSocketAddress createAbstract(String name) {
+ byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
+ // Abstract sockets have a path that starts with (byte) 0.
+ byte[] path = new byte[nameBytes.length + 1];
+ System.arraycopy(nameBytes, 0, path, 1, nameBytes.length);
+ return new UnixSocketAddress(path);
+ }
+
+ /**
+ * Creates a named, filesystem AF_UNIX socket address.
+ */
+ public static UnixSocketAddress createFileSystem(String pathName) {
+ byte[] pathNameBytes = pathName.getBytes(StandardCharsets.UTF_8);
+ // File system sockets have a path that ends with (byte) 0.
+ byte[] path = new byte[pathNameBytes.length + 1];
+ System.arraycopy(pathNameBytes, 0, path, 0, pathNameBytes.length);
+ return new UnixSocketAddress(path);
+ }
+
+ /**
+ * Creates an unnamed, filesystem AF_UNIX socket address.
+ */
+ public static UnixSocketAddress createUnnamed() {
+ return new UnixSocketAddress(UNNAMED_PATH);
+ }
+
+ /** Used for testing. */
+ public byte[] getSunPath() {
+ if (sun_path.length == 0) {
+ return sun_path;
+ }
+ byte[] sunPathCopy = new byte[sun_path.length];
+ System.arraycopy(sun_path, 0, sunPathCopy, 0, sun_path.length);
+ return sunPathCopy;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ UnixSocketAddress that = (UnixSocketAddress) o;
+ return Arrays.equals(sun_path, that.sun_path);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(sun_path);
+ }
+
+ @Override
+ public String toString() {
+ return "UnixSocketAddress[" +
+ "sun_path=" + Arrays.toString(sun_path) +
+ ']';
+ }
+}
diff --git a/luni/src/main/java/java/lang/StrictMath.java b/luni/src/main/java/java/lang/StrictMath.java
index 2e848f2..5c9638b 100644
--- a/luni/src/main/java/java/lang/StrictMath.java
+++ b/luni/src/main/java/java/lang/StrictMath.java
@@ -300,7 +300,7 @@
int ix, hx, id;
final long bits = Double.doubleToRawLongBits(x);
- hx = (int) (bits >>> 32);
+ hx = highBits(bits);
ix = hx & 0x7fffffff;
if (ix >= 0x44100000) { /* if |x| >= 2^66 */
if (ix > 0x7ff00000 || (ix == 0x7ff00000 && (((int) bits) != 0))) {
@@ -404,12 +404,12 @@
final long yBits = Double.doubleToRawLongBits(y);
final long xBits = Double.doubleToRawLongBits(x);
- hx = (int) (xBits >>> 32); // __HI(x);
+ hx = highBits(xBits); // __HI(x);
ix = hx & 0x7fffffff;
- lx = (int) xBits; // __LO(x);
- hy = (int) (yBits >>> 32); // __HI(y);
+ lx = lowBits(xBits); // __LO(x);
+ hy = highBits(yBits); // __HI(y);
iy = hy & 0x7fffffff;
- ly = (int) yBits; // __LO(y);
+ ly = lowBits(yBits); // __LO(y);
if (((ix | ((lx | -lx) >> 31)) > 0x7ff00000)
|| ((iy | ((ly | -ly) >> 31)) > 0x7ff00000)) { /* x or y is NaN */
return x + y;
@@ -525,14 +525,14 @@
int sign; // caution: should be unsigned
long bits = Double.doubleToRawLongBits(x);
- hx = (int) (bits >>> 32);
+ hx = highBits(bits);
sign = hx & 0x80000000; /* sign= sign(x) */
hx ^= sign;
if (hx >= 0x7ff00000) {
return (x + x); /* ieee_cbrt(NaN,INF) is itself */
}
- if ((hx | ((int) bits)) == 0) {
+ if ((hx | lowBits(bits)) == 0) {
return x; /* ieee_cbrt(0) is itself */
}
@@ -599,8 +599,79 @@
* <li>{@code ceil(-infinity) = -infinity}</li>
* <li>{@code ceil(NaN) = NaN}</li>
* </ul>
+ *
+ * @param d
+ * the double value whose ceiling will be computed.
+ * @return the ceiling of the argument.
*/
- public static native double ceil(double d);
+ public static double ceil(double d) {
+ final long bits = Double.doubleToRawLongBits(d);
+ int highBits = highBits(bits); // high word of d
+ int lowBits = lowBits(bits); // low word of d
+ int exp = ((highBits >> 20) & 0x7ff) - 0x3ff; // value of exponent
+
+ /* negative exponent */
+ if (exp < 0) {
+ if (HUGE + d > 0.0) {
+ if (highBits < 0) { // if |d| < 1 return -0
+ highBits = 0x80000000;
+ } else if ((highBits | lowBits) != 0) {
+ // raise inexact if d != 0, this is ignored by Java
+ highBits = 0x3ff00000; // return 1
+ }
+ lowBits = 0;
+ }
+ }
+ /* exponent in range [0, 20) */
+ else if (exp < 20) {
+ int i = (0x000fffff) >> exp; // careful, should be unsigned
+ /* d is integral */
+ if (((highBits & i) | lowBits) == 0) {
+ return d;
+ }
+ if (HUGE + d > 0.0) { // raise inexact flag: this is ignored by Java
+ if (highBits > 0) {
+ highBits += (0x00100000) >> exp;
+ }
+ highBits &= (~i);
+ lowBits = 0;
+ }
+ }
+ /* exponent in range (51, inf) */
+ else if (exp > 51) {
+ /* inf or NaN */
+ if (exp == 0x400) {
+ return d + d;
+ }
+ return d; // d is integral
+ }
+ /* exponent in range [21,51] */
+ else {
+ int i = (0xffffffff) >>> (exp - 20); // careful, should be unsigned
+ /* d is integral */
+ if ((lowBits & i) == 0) {
+ return d;
+ }
+ /* raise inexact flag: this is ignored by Java */
+ if (HUGE + d > 0.0) {
+ if (highBits > 0) {
+ if (exp == 20) {
+ highBits += 1;
+ } else {
+ // careful, j should be unsigned
+ int j = (int)(lowBits + (1 << (52 - exp)));
+ if ((lowBits < 0) && (j >= 0)) {
+ highBits += 1; // carry occurred
+ }
+ lowBits = j;
+ }
+ }
+ lowBits &= (~i);
+ }
+ }
+ /* combine highBits and unsigned lowBits for final result */
+ return Double.longBitsToDouble(((long)highBits << 32) + (lowBits & 0xFFFFFFFFL));
+ }
private static final long ONEBITS = Double.doubleToRawLongBits(1.00000000000000000000e+00)
& 0x00000000ffffffffL;
@@ -624,7 +695,7 @@
double t, w;
int ix;
final long bits = Double.doubleToRawLongBits(x);
- ix = (int) (bits >>> 32) & 0x7fffffff;
+ ix = highBits(bits) & 0x7fffffff;
/* x is INF or NaN */
if (ix >= 0x7ff00000) {
@@ -715,8 +786,8 @@
int k = 0, xsb;
int hx; // should be unsigned, be careful!
final long bits = Double.doubleToRawLongBits(x);
- int lowBits = (int) bits;
- int highBits = (int) (bits >>> 32);
+ int lowBits = lowBits(bits);
+ int highBits = highBits(bits);
hx = highBits & 0x7fffffff;
xsb = (highBits >>> 31) & 1;
@@ -814,8 +885,8 @@
int k, xsb;
long yBits = 0;
final long bits = Double.doubleToRawLongBits(x);
- int highBits = (int) (bits >>> 32);
- int lowBits = (int) (bits);
+ int highBits = highBits(bits);
+ int lowBits = lowBits(bits);
int hx = highBits & 0x7fffffff; // caution: should be unsigned!
xsb = highBits & 0x80000000; /* sign bit of x */
y = xsb == 0 ? x : -x; /* y = |x| */
@@ -930,8 +1001,79 @@
* <li>{@code floor(-infinity) = -infinity}</li>
* <li>{@code floor(NaN) = NaN}</li>
* </ul>
+ *
+ * @param d
+ * the double value whose floor will be computed.
+ * @return the floor of the argument.
*/
- public static native double floor(double d);
+ public static double floor(double d) {
+ final long bits = Double.doubleToRawLongBits(d);
+ int highBits = highBits(bits); // high word of d
+ int lowBits = lowBits(bits); // low word of d
+ int exp = ((highBits >> 20) & 0x7ff) - 0x3ff; // value of exponent
+
+ /* negative exponent */
+ if (exp < 0) {
+ if (HUGE + d > 0.0) {
+ if (highBits >= 0) { // if |d| < 1
+ highBits = 0;
+ } else if (((highBits & 0x7fffffff) | lowBits) != 0) {
+ // raise inexact if d != 0, this is ignored by Java
+ highBits = 0xbff00000;
+ }
+ lowBits = 0;
+ }
+ }
+ /* exponent in range [0, 20) */
+ else if (exp < 20) {
+ int i = (0x000fffff) >> exp; // careful, should be unsigned
+ /* d is integral */
+ if (((highBits & i) | lowBits) == 0) {
+ return d;
+ }
+ if (HUGE + d > 0.0) { // raise inexact flag: this is ignored by Java
+ if (highBits < 0) {
+ highBits += (0x00100000) >> exp;
+ }
+ highBits &= (~i);
+ lowBits = 0;
+ }
+ }
+ /* exponent in range (51, inf) */
+ else if (exp > 51) {
+ /* inf or NaN */
+ if (exp == 0x400) {
+ return d + d;
+ }
+ return d; // d is integral
+ }
+ /* exponent in range [21,51] */
+ else {
+ int i = (0xffffffff) >>> (exp - 20); // careful, should be unsigned
+ /* d is integral */
+ if ((lowBits & i) == 0) {
+ return d;
+ }
+ /* raise inexact flag: this is ignored by java */
+ if (HUGE + d > 0.0) {
+ if (highBits < 0) {
+ if (exp == 20) {
+ highBits += 1;
+ } else {
+ // careful, j should be unsigned
+ int j = (int)(lowBits + (1 << (52 - exp)));
+ if ((lowBits < 0) && (j >= 0)) {
+ highBits += 1; // carry occurred
+ }
+ lowBits = j;
+ }
+ }
+ lowBits &= (~i);
+ }
+ }
+ /* combine highBits and unsigned lowBits for final result */
+ return Double.longBitsToDouble(((long)highBits << 32) + (lowBits & 0xFFFFFFFFL));
+ }
/**
* Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
@@ -1016,8 +1158,8 @@
int lx; // watch out, should be unsigned
long bits = Double.doubleToRawLongBits(x);
- hx = (int) (bits >>> 32); /* high word of x */
- lx = (int) bits; /* low word of x */
+ hx = highBits(bits); /* high word of x */
+ lx = lowBits(bits); /* low word of x */
if (hx < 0x00100000) { /* x < 2**-1022 */
if (((hx & 0x7fffffff) | lx) == 0) {
@@ -1031,7 +1173,7 @@
k -= 54;
x *= TWO54; /* subnormal number, scale up x */
bits = Double.doubleToRawLongBits(x);
- hx = (int) (bits >>> 32); /* high word of x */
+ hx = highBits(bits); /* high word of x */
}
if (hx >= 0x7ff00000) {
@@ -1119,8 +1261,8 @@
int i, k = 0, hx;
int lx; // careful: lx should be unsigned!
long bits = Double.doubleToRawLongBits(x);
- hx = (int) (bits >> 32); /* high word of x */
- lx = (int) bits; /* low word of x */
+ hx = highBits(bits); /* high word of x */
+ lx = lowBits(bits); /* low word of x */
if (hx < 0x00100000) { /* x < 2**-1022 */
if (((hx & 0x7fffffff) | lx) == 0) {
return -TWO54 / 0.0; /* ieee_log(+-0)=-inf */
@@ -1133,7 +1275,7 @@
k -= 54;
x *= TWO54; /* subnormal number, scale up x */
bits = Double.doubleToRawLongBits(x);
- hx = (int) (bits >> 32); /* high word of x */
+ hx = highBits(bits); /* high word of x */
}
if (hx >= 0x7ff00000) {
@@ -1186,7 +1328,7 @@
int k, hx, hu = 0, ax;
final long bits = Double.doubleToRawLongBits(x);
- hx = (int) (bits >>> 32); /* high word of x */
+ hx = highBits(bits); /* high word of x */
ax = hx & 0x7fffffff;
k = 1;
@@ -1221,13 +1363,13 @@
if (hx < 0x43400000) {
u = 1.0 + x;
uBits = Double.doubleToRawLongBits(u);
- hu = (int) (uBits >>> 32);
+ hu = highBits(uBits);
k = (hu >> 20) - 1023;
c = (k > 0) ? 1.0 - (u - x) : x - (u - 1.0);/* correction term */
c /= u;
} else {
uBits = Double.doubleToRawLongBits(x);
- hu = (int) (uBits >>> 32);
+ hu = highBits(uBits);
k = (hu >> 20) - 1023;
c = 0;
}
@@ -1603,7 +1745,7 @@
int ix, jx;
final long bits = Double.doubleToRawLongBits(x);
- jx = (int) (bits >>> 32);
+ jx = highBits(bits);
ix = jx & 0x7fffffff;
/* x is INF or NaN */
@@ -1720,7 +1862,7 @@
final long bits = Double.doubleToRawLongBits(x);
/* High word of |x|. */
- jx = (int) (bits >>> 32);
+ jx = highBits(bits);
ix = jx & 0x7fffffff;
/* x is INF or NaN */
@@ -2091,4 +2233,14 @@
}
return 0;
}
+
+ // Returns the high word of the argument as an int.
+ private static int highBits(long bits) {
+ return (int) (bits >>> 32);
+ }
+
+ // Returns the low word of the argument as an int.
+ private static int lowBits(long bits) {
+ return (int) bits;
+ }
}
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index c5d9a89..81da814 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -845,14 +845,22 @@
// On Android, each app gets its own temporary directory.
// (See android.app.ActivityThread.) This is just a fallback default,
// useful only on the host.
- p.put("java.io.tmpdir", "/tmp");
+ // We check first if the property has not been set already: note that it
+ // can only be set from the command line through the '-Djava.io.tmpdir=' option.
+ if (!unchangeableSystemProperties.containsKey("java.io.tmpdir")) {
+ p.put("java.io.tmpdir", "/tmp");
+ }
// Android has always had an empty "user.home" (see docs for getProperty).
// This is not useful for normal android apps which need to use android specific
// APIs such as {@code Context.getFilesDir} and {@code Context.getCacheDir} but
// we make it changeable for backward compatibility, so that they can change it
// to a writeable location if required.
- p.put("user.home", "");
+ // We check first if the property has not been set already: note that it
+ // can only be set from the command line through the '-Duser.home=' option.
+ if (!unchangeableSystemProperties.containsKey("user.home")) {
+ p.put("user.home", "");
+ }
}
private static Properties createSystemProperties() {
diff --git a/luni/src/main/java/java/net/InetUnixAddress.java b/luni/src/main/java/java/net/InetUnixAddress.java
deleted file mode 100644
index 51236e2..0000000
--- a/luni/src/main/java/java/net/InetUnixAddress.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2013 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 java.net;
-
-import java.nio.charset.StandardCharsets;
-
-import static android.system.OsConstants.*;
-
-/**
- * An AF_UNIX address. See {@link InetAddress}.
- * @hide
- */
-public final class InetUnixAddress extends InetAddress {
- /**
- * Constructs an AF_UNIX InetAddress for the given path.
- */
- public InetUnixAddress(String path) {
- this(path.getBytes(StandardCharsets.UTF_8));
- }
-
- /**
- * Constructs an AF_UNIX InetAddress for the given path.
- */
- public InetUnixAddress(byte[] path) {
- super(AF_UNIX, path, null);
- }
-
- /**
- * Returns a string form of this InetAddress.
- */
- @Override public String toString() {
- return "InetUnixAddress[" + new String(ipaddress, StandardCharsets.UTF_8) + "]";
- }
-}
diff --git a/luni/src/main/java/java/net/MulticastSocket.java b/luni/src/main/java/java/net/MulticastSocket.java
index 24e66c5..2d8fdd6 100644
--- a/luni/src/main/java/java/net/MulticastSocket.java
+++ b/luni/src/main/java/java/net/MulticastSocket.java
@@ -41,7 +41,9 @@
* @throws IOException if an error occurs.
*/
public MulticastSocket() throws IOException {
+ super((SocketAddress) null);
setReuseAddress(true);
+ bind(null);
}
/**
@@ -51,8 +53,7 @@
* @throws IOException if an error occurs.
*/
public MulticastSocket(int port) throws IOException {
- super(port);
- setReuseAddress(true);
+ this(new InetSocketAddress(Inet6Address.ANY, port));
}
/**
@@ -64,8 +65,11 @@
* @throws IOException if an error occurs.
*/
public MulticastSocket(SocketAddress localAddress) throws IOException {
- super(localAddress);
+ super((SocketAddress) null);
setReuseAddress(true);
+ if (localAddress != null) {
+ bind(localAddress);
+ }
}
/**
diff --git a/luni/src/main/java/java/nio/ByteOrder.java b/luni/src/main/java/java/nio/ByteOrder.java
index 286c970..508c8ef 100644
--- a/luni/src/main/java/java/nio/ByteOrder.java
+++ b/luni/src/main/java/java/nio/ByteOrder.java
@@ -32,13 +32,10 @@
*/
public static final ByteOrder LITTLE_ENDIAN;
- private static native boolean isLittleEndian();
-
static {
- boolean isLittleEndian = isLittleEndian();
- BIG_ENDIAN = new ByteOrder("BIG_ENDIAN", isLittleEndian);
- LITTLE_ENDIAN = new ByteOrder("LITTLE_ENDIAN", !isLittleEndian);
- NATIVE_ORDER = isLittleEndian ? LITTLE_ENDIAN : BIG_ENDIAN;
+ BIG_ENDIAN = new ByteOrder("BIG_ENDIAN", true /* needs swap */);
+ LITTLE_ENDIAN = new ByteOrder("LITTLE_ENDIAN", false /* needs swap */);
+ NATIVE_ORDER = LITTLE_ENDIAN;
}
private final String name;
diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
index 5dfdd9f..38be0c2 100644
--- a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
+++ b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
@@ -202,7 +202,7 @@
private void setPosition(CharBuffer out) {
if (out.hasArray()) {
- out.position(out.position() + data[OUTPUT_OFFSET] - out.arrayOffset());
+ out.position(out.position() + data[OUTPUT_OFFSET]);
} else {
out.put(output, 0, data[OUTPUT_OFFSET]);
}
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index 875955d..a9f6333 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -2226,7 +2226,13 @@
}
}
- final String languageCode = Builder.normalizeAndValidateLanguage(subtags[0], strict);
+ // The BCP-47 language tag "und" is mapped to the Locale languageCode "" here in
+ // forLanguageTag() for compatibility with JDK behaviour.
+ String languageCode = Builder.normalizeAndValidateLanguage(subtags[0], strict);
+ if (UNDETERMINED_LANGUAGE.equals(languageCode)) {
+ languageCode = "";
+ }
+
String scriptCode = "";
int nextSubtag = 1;
if (lastSubtag > nextSubtag) {
diff --git a/luni/src/main/java/java/util/jar/StrictJarFile.java b/luni/src/main/java/java/util/jar/StrictJarFile.java
index a73ca2a..e4cda0e 100644
--- a/luni/src/main/java/java/util/jar/StrictJarFile.java
+++ b/luni/src/main/java/java/util/jar/StrictJarFile.java
@@ -40,6 +40,8 @@
*/
public final class StrictJarFile {
+ private final String fileName;
+
private final long nativeHandle;
// NOTE: It's possible to share a file descriptor with the native
@@ -55,24 +57,34 @@
private boolean closed;
public StrictJarFile(String fileName) throws IOException, SecurityException {
+ this.fileName = fileName;
this.nativeHandle = nativeOpenJarFile(fileName);
this.raf = new RandomAccessFile(fileName, "r");
try {
// Read the MANIFEST and signature files up front and try to
// parse them. We never want to accept a JAR File with broken signatures
- // or manifests, so it's best to throw as early as possible.
+ // or manifests, so it's best to throw as early as possible. It is valid
+ // for a jar file to be missing a manifest and it's treated the same
+ // as any other unsigned jar.
HashMap<String, byte[]> metaEntries = getMetaEntries();
- this.manifest = new Manifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
- this.verifier = new JarVerifier(fileName, manifest, metaEntries);
- Set<String> files = this.manifest.getEntries().keySet();
- for (String file : files) {
- if (findEntry(file) == null) {
- throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+ byte[] manifestBytes = metaEntries.get(JarFile.MANIFEST_NAME);
+ if (manifestBytes == null) {
+ this.manifest = null;
+ this.verifier = null;
+ isSigned = false;
+ } else {
+ this.manifest = new Manifest(manifestBytes, true);
+ this.verifier = new JarVerifier(fileName, manifest, metaEntries);
+ Set<String> files = this.manifest.getEntries().keySet();
+ for (String file : files) {
+ if (findEntry(file) == null) {
+ throw new SecurityException(
+ fileName + ": File " + file + " in manifest does not exist");
+ }
}
+ isSigned = verifier.readCertificates() && verifier.isSignedJar();
}
-
- isSigned = verifier.readCertificates() && verifier.isSignedJar();
} catch (IOException | SecurityException e) {
nativeClose(this.nativeHandle);
IoUtils.closeQuietly(this.raf);
@@ -82,6 +94,10 @@
guard.open("close");
}
+ public String getName() {
+ return fileName;
+ }
+
public Manifest getManifest() {
return manifest;
}
diff --git a/luni/src/main/java/java/util/zip/InflaterInputStream.java b/luni/src/main/java/java/util/zip/InflaterInputStream.java
index e5ad3db..a558f34 100644
--- a/luni/src/main/java/java/util/zip/InflaterInputStream.java
+++ b/luni/src/main/java/java/util/zip/InflaterInputStream.java
@@ -193,6 +193,8 @@
} else {
if ((len = in.read(buf)) > 0) {
inf.setInput(buf, 0, len);
+ } else if (len == -1) {
+ throw new EOFException("Unexpected end of stream.");
}
}
}
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index f3ca74e..f254ba2 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -112,7 +112,11 @@
@Override
public void close() throws IOException {
if (!closed) {
- closeEntry(); // Close the current entry
+ try {
+ closeEntry(); // Close the current entry
+ } catch (IOException ignored) {
+ }
+
super.close();
}
}
diff --git a/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
index e2afa61..7278175 100644
--- a/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
+++ b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
@@ -65,7 +65,7 @@
* This is the internal API that implements the functionality of
* DateUtils.getRelativeTimeSpanString(long, long, long, int), which is to
* return a string describing 'time' as a time relative to 'now' such as
- * '5 minutes ago', or 'in 2 days'. More examples can be found in DateUtils'
+ * '5 minutes ago', or 'In 2 days'. More examples can be found in DateUtils'
* doc.
*
* In the implementation below, it selects the appropriate time unit based on
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 532493a..0923c85 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -28,6 +28,7 @@
import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import static android.system.OsConstants.*;
@@ -58,7 +59,7 @@
}
}
- @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException {
+ @Override public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return tagSocket(os.accept(fd, peerAddress));
}
diff --git a/luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java b/luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java
new file mode 100644
index 0000000..2f26b14
--- /dev/null
+++ b/luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015 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.io;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.util.jar.JarFile;
+import java.util.jar.StrictJarFile;
+import java.util.zip.ZipEntry;
+import libcore.net.url.JarHandler;
+
+/**
+ * A {@link URLStreamHandler} for a specific class path {@link JarFile}. This class avoids the need
+ * to open a jar file multiple times to read resources if the jar file can be held open. The
+ * {@link URLConnection} objects created are a subclass of {@link JarURLConnection}.
+ *
+ * <p>Use {@link #getEntryUrlOrNull(String)} to obtain a URL backed by this stream handler.
+ */
+public class ClassPathURLStreamHandler extends JarHandler {
+ private final String fileUri;
+ private final StrictJarFile jarFile;
+
+ public ClassPathURLStreamHandler(String jarFileName) throws IOException {
+ // We use StrictJarFile because it is much less heap memory hungry than ZipFile / JarFile.
+ jarFile = new StrictJarFile(jarFileName);
+
+ // File.toURI() is compliant with RFC 1738 in always creating absolute path names. If we
+ // construct the URL by concatenating strings, we might end up with illegal URLs for relative
+ // names.
+ this.fileUri = new File(jarFileName).toURI().toString();
+ }
+
+ /**
+ * Returns a URL backed by this stream handler for the named resource, or {@code null} if the
+ * entry cannot be found under the exact name presented.
+ */
+ public URL getEntryUrlOrNull(String entryName) {
+ if (jarFile.findEntry(entryName) != null) {
+ try {
+ // We rely on the URL/the stream handler to deal with any url encoding necessary here, and
+ // we assume it is completely reversible.
+ return new URL("jar", null, -1, fileUri + "!/" + entryName, this);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Invalid entry name", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if entry with specified name exists and stored (not compressed),
+ * and false otherwise.
+ */
+ public boolean isEntryStored(String entryName) {
+ ZipEntry entry = jarFile.findEntry(entryName);
+ return entry != null && entry.getMethod() == ZipEntry.STORED;
+ }
+
+ @Override
+ protected URLConnection openConnection(URL url) throws IOException {
+ return new ClassPathURLConnection(url, jarFile);
+ }
+
+ public void close() throws IOException {
+ jarFile.close();
+ }
+
+ private static class ClassPathURLConnection extends JarURLConnection {
+
+ private final StrictJarFile strictJarFile;
+ private ZipEntry jarEntry;
+ private InputStream jarInput;
+ private boolean closed;
+ private JarFile jarFile;
+
+ public ClassPathURLConnection(URL url, StrictJarFile strictJarFile)
+ throws MalformedURLException {
+ super(url);
+ this.strictJarFile = strictJarFile;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ if (!connected) {
+ this.jarEntry = strictJarFile.findEntry(getEntryName());
+ if (jarEntry == null) {
+ throw new FileNotFoundException(
+ "URL does not correspond to an entry in the zip file. URL=" + url
+ + ", zipfile=" + strictJarFile.getName());
+ }
+ connected = true;
+ }
+ }
+
+ @Override
+ public JarFile getJarFile() throws IOException {
+ connect();
+
+ // This is expensive because we only pretend that we wrap a JarFile.
+ if (jarFile == null) {
+ jarFile = new JarFile(strictJarFile.getName());
+ }
+ return jarFile;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if (closed) {
+ throw new IllegalStateException("JarURLConnection InputStream has been closed");
+ }
+ connect();
+ if (jarInput != null) {
+ return jarInput;
+ }
+ return jarInput = new FilterInputStream(strictJarFile.getInputStream(jarEntry)) {
+ @Override
+ public void close() throws IOException {
+ super.close();
+ closed = true;
+ }
+ };
+ }
+
+ /**
+ * Returns the content type of the entry based on the name of the entry. Returns
+ * non-null results ("content/unknown" for unknown types).
+ *
+ * @return the content type
+ */
+ @Override
+ public String getContentType() {
+ String cType = guessContentTypeFromName(getEntryName());
+ if (cType == null) {
+ cType = "content/unknown";
+ }
+ return cType;
+ }
+
+ @Override
+ public int getContentLength() {
+ try {
+ connect();
+ return (int) getJarEntry().getSize();
+ } catch (IOException e) {
+ // Ignored
+ }
+ return -1;
+ }
+ }
+}
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index cb77573..2dfb418 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -50,7 +50,7 @@
this.os = os;
}
- public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); }
+ public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); }
public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return os.android_getaddrinfo(node, hints, netId); }
public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); }
@@ -169,6 +169,7 @@
public void tcsendbreak(FileDescriptor fd, int duration) throws ErrnoException { os.tcsendbreak(fd, duration); }
public int umask(int mask) { return os.umask(mask); }
public StructUtsname uname() { return os.uname(); }
+ public void unlink(String pathname) throws ErrnoException { os.unlink(pathname); }
public void unsetenv(String name) throws ErrnoException { os.unsetenv(name); }
public int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return os.waitpid(pid, status, options); }
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.write(fd, buffer); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 6d28b95..3cae357 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -41,7 +41,7 @@
import java.nio.ByteBuffer;
public interface Os {
- public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
+ public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException;
public boolean access(String path, int mode) throws ErrnoException;
public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
@@ -162,6 +162,7 @@
public void tcsendbreak(FileDescriptor fd, int duration) throws ErrnoException;
public int umask(int mask);
public StructUtsname uname();
+ public void unlink(String pathname) throws ErrnoException;
public void unsetenv(String name) throws ErrnoException;
public int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 151809d..b8bbc85 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -44,7 +44,7 @@
public final class Posix implements Os {
Posix() { }
- public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
+ public native FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException;
public native boolean access(String path, int mode) throws ErrnoException;
public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
@@ -252,6 +252,7 @@
}
private native int umaskImpl(int mask);
public native StructUtsname uname();
+ public native void unlink(String pathname) throws ErrnoException;
public native void unsetenv(String name) throws ErrnoException;
public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java
index fd8570c..d2cb9ab 100644
--- a/luni/src/main/java/libcore/util/ZoneInfoDB.java
+++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java
@@ -18,17 +18,11 @@
import android.system.ErrnoException;
import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.TimeZone;
import libcore.io.BufferIterator;
-import libcore.io.IoUtils;
import libcore.io.MemoryMappedFile;
/**
@@ -253,6 +247,13 @@
public boolean hasTimeZone(String id) throws IOException {
return cache.get(id) != null;
}
+
+ @Override protected void finalize() throws Throwable {
+ if (mappedFile != null) {
+ mappedFile.close();
+ }
+ super.finalize();
+ }
}
private ZoneInfoDB() {
diff --git a/luni/src/main/java/org/apache/harmony/security/x501/AttributeTypeAndValue.java b/luni/src/main/java/org/apache/harmony/security/x501/AttributeTypeAndValue.java
index 171d9c2..71afa9d 100644
--- a/luni/src/main/java/org/apache/harmony/security/x501/AttributeTypeAndValue.java
+++ b/luni/src/main/java/org/apache/harmony/security/x501/AttributeTypeAndValue.java
@@ -251,9 +251,10 @@
sb.append('=');
if (value.escapedString == value.getHexString()) {
sb.append(value.getHexString().toUpperCase(Locale.US));
- } else if (value.escapedString.length() != value.rawString.length()) {
+ } else if (value.escapedString.length() != value.rawString.length()
+ || value.hasQEN) {
// was escaped
- value.appendQEString(sb);
+ value.appendQuotedString(sb);
} else {
sb.append(value.escapedString);
}
diff --git a/luni/src/main/java/org/apache/harmony/security/x501/AttributeValue.java b/luni/src/main/java/org/apache/harmony/security/x501/AttributeValue.java
index b3eb200..b38b78a 100644
--- a/luni/src/main/java/org/apache/harmony/security/x501/AttributeValue.java
+++ b/luni/src/main/java/org/apache/harmony/security/x501/AttributeValue.java
@@ -51,16 +51,16 @@
public byte[] bytes; //FIXME remove??? bytes to be encoded
- public boolean hasQE; // raw string contains '"' or '\'
+ public boolean hasQEN; // raw string contains '"', '\' or '\n'
public final String rawString;
- public AttributeValue(String parsedString, boolean hasQorE, ObjectIdentifier oid) {
+ public AttributeValue(String parsedString, boolean mustBeOutputWithQuotes, ObjectIdentifier oid) {
wasEncoded = false;
- this.hasQE = hasQorE;
+ this.hasQEN = mustBeOutputWithQuotes;
this.rawString = parsedString;
- this.escapedString = makeEscaped(rawString); // overwrites hasQE
+ this.escapedString = makeEscaped(rawString); // overwrites hasQEN
int tag;
if (oid == AttributeTypeAndValue.EMAILADDRESS || oid == AttributeTypeAndValue.DC) {
@@ -180,9 +180,9 @@
return (Collection<?>) new ASN1SetOf(type).decode(encoded);
}
- public void appendQEString(StringBuilder sb) {
+ public void appendQuotedString(StringBuilder sb) {
sb.append('"');
- if (hasQE) {
+ if (hasQEN) {
char c;
for (int i = 0; i < rawString.length(); i++) {
c = rawString.charAt(i);
@@ -247,11 +247,16 @@
case '"':
case '\\':
- hasQE = true;
buf.append('\\');
buf.append(ch);
+ hasQEN = true;
break;
-
+ case '\n':
+ // New line is treated specially in other contexts (that's we need to set hasQEN)
+ // but shouldn't be escaped (see rfc2253, rfc 1779 and RI).
+ buf.append(ch);
+ hasQEN = true;
+ break;
case ',':
case '+':
case '<':
diff --git a/luni/src/main/java/org/apache/harmony/security/x509/DNParser.java b/luni/src/main/java/org/apache/harmony/security/x509/DNParser.java
index 2f6fcb9..f15cc81 100644
--- a/luni/src/main/java/org/apache/harmony/security/x509/DNParser.java
+++ b/luni/src/main/java/org/apache/harmony/security/x509/DNParser.java
@@ -50,8 +50,8 @@
/** distinguished name chars */
private final char[] chars;
- /** raw string contains '"' or '\' */
- private boolean hasQE;
+ /** raw string contains '"', '\' or '\n' */
+ private boolean hasQEN;
/** DER encoding of currently parsed item */
private byte[] encoded;
@@ -67,7 +67,7 @@
* Returns the next attribute type: (ALPHA 1*keychar) / oid
*/
private String nextAT() throws IOException {
- hasQE = false; // reset
+ hasQEN = false; // reset
// skip preceding space chars, they can present after
// comma or semicolon (compatibility with RFC 1779)
@@ -271,7 +271,11 @@
switch (ch) {
case '"':
case '\\':
- hasQE = true;
+ // <CR> Is not a special character in RFC2253, but it is in RFC1779. The Javadoc in the RI for
+ // X500Principal says that names are according to RFC2253 but ones according to RFC1779
+ // should also be accepted.
+ case '\n':
+ hasQEN = true;
return ch;
case ',':
case '=':
@@ -403,7 +407,7 @@
switch (chars[pos]) {
case '"':
- atav.add(new AttributeTypeAndValue(oid, new AttributeValue(quotedAV(), hasQE, oid)));
+ atav.add(new AttributeTypeAndValue(oid, new AttributeValue(quotedAV(), hasQEN, oid)));
break;
case '#':
atav.add(new AttributeTypeAndValue(oid, new AttributeValue(hexAV(), encoded)));
@@ -416,7 +420,7 @@
break;
default:
atav.add(new AttributeTypeAndValue(oid,
- new AttributeValue(escapedAV(), hasQE, oid)));
+ new AttributeValue(escapedAV(), hasQEN, oid)));
}
if (pos >= chars.length) {
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
index 7215306..f2d0f8a 100644
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -61,10 +61,6 @@
addressLength = 16;
sin_port = ntohs(sin6.sin6_port);
scope_id = sin6.sin6_scope_id;
- } else if (ss.ss_family == AF_UNIX) {
- const sockaddr_un& sun = reinterpret_cast<const sockaddr_un&>(ss);
- rawAddress = &sun.sun_path;
- addressLength = strlen(sun.sun_path);
} else {
// We can't throw SocketException. We aren't meant to see bad addresses, so seeing one
// really does imply an internal error.
@@ -83,14 +79,6 @@
env->SetByteArrayRegion(byteArray.get(), 0, addressLength,
reinterpret_cast<const jbyte*>(rawAddress));
- if (ss.ss_family == AF_UNIX) {
- // Note that we get here for AF_UNIX sockets on accept(2). The unix(7) man page claims
- // that the peer's sun_path will contain the path, but in practice it doesn't, and the
- // peer length is returned as 2 (meaning only the sun_family field was set).
- static jmethodID ctor = env->GetMethodID(JniConstants::inetUnixAddressClass, "<init>", "([B)V");
- return env->NewObject(JniConstants::inetUnixAddressClass, ctor, byteArray.get());
- }
-
static jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inetAddressClass,
"getByAddress", "(Ljava/lang/String;[BI)Ljava/net/InetAddress;");
if (getByAddressMethod == NULL) {
@@ -118,7 +106,7 @@
}
// Check this is an address family we support.
- if (ss.ss_family != AF_INET && ss.ss_family != AF_INET6 && ss.ss_family != AF_UNIX) {
+ if (ss.ss_family != AF_INET && ss.ss_family != AF_INET6) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
"inetAddressToSockaddr bad family: %i", ss.ss_family);
return false;
@@ -132,25 +120,6 @@
return false;
}
- // Handle the AF_UNIX special case.
- if (ss.ss_family == AF_UNIX) {
- sockaddr_un& sun = reinterpret_cast<sockaddr_un&>(ss);
-
- size_t path_length = env->GetArrayLength(addressBytes.get());
- if (path_length >= sizeof(sun.sun_path)) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "inetAddressToSockaddr path too long for AF_UNIX: %i", path_length);
- return false;
- }
-
- // Copy the bytes...
- jbyte* dst = reinterpret_cast<jbyte*>(&sun.sun_path);
- memset(dst, 0, sizeof(sun.sun_path));
- env->GetByteArrayRegion(addressBytes.get(), 0, path_length, dst);
- sa_len = sizeof(sun.sun_path);
- return true;
- }
-
// TODO: bionic's getnameinfo(3) seems to want its length parameter to be exactly
// sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an
// IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
index 6b720d4..db1ad1f 100644
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -17,25 +17,24 @@
#include "jni.h"
#include <sys/socket.h>
-// Convert from sockaddr_storage to Inet4Address (AF_INET), Inet6Address (AF_INET6), or
-// InetUnixAddress (AF_UNIX). If 'port' is non-NULL and the address family includes a notion
+// Convert from sockaddr_storage to Inet4Address (AF_INET) or Inet6Address (AF_INET6).
+// If 'port' is non-NULL and the address family includes a notion
// of port number, *port will be set to the port number.
jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, int* port);
-// Convert from InetAddress to sockaddr_storage. An InetUnixAddress will be converted to
-// an AF_UNIX sockaddr_un. An Inet6Address will be converted to an AF_INET6 sockaddr_in6.
-// An Inet4Address will be converted to an IPv4-mapped AF_INET6 sockaddr_in6. This is what
-// you want if you're about to perform an operation on a socket, since all our sockets
-// are AF_INET6.
+// Convert from InetAddress to sockaddr_storage. An Inet6Address will be converted to an
+// AF_INET6 sockaddr_in6. An Inet4Address will be converted to an IPv4-mapped AF_INET6
+// sockaddr_in6. This is what you want if you're about to perform an operation on a socket,
+// since all our sockets are AF_INET6.
bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port,
sockaddr_storage& ss, socklen_t& sa_len);
-// Convert from InetAddress to sockaddr_storage. An InetUnixAddress will be converted to
-// an AF_UNIX sockaddr_un. An Inet6Address will be converted to an AF_INET6 sockaddr_in6.
-// An Inet4Address will be converted to a sockaddr_in. This is probably only useful for
-// getnameinfo(2), where we'll be presenting the result to the user and the user may actually
-// care whether the original address was pure IPv4 or an IPv4-mapped IPv6 address, and
-// for the MCAST_JOIN_GROUP, MCAST_LEAVE_GROUP, and other multicast socket options.
+// Convert from InetAddress to sockaddr_storage. An Inet6Address will be converted to an
+// AF_INET6 sockaddr_in6. An Inet4Address will be converted to a sockaddr_in. This is probably
+// only useful for getnameinfo(2), where we'll be presenting the result to the user and the
+// user may actually care whether the original address was pure IPv4 or an IPv4-mapped IPv6
+// address, and for the MCAST_JOIN_GROUP, MCAST_LEAVE_GROUP, and other multicast socket
+// options.
bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port,
sockaddr_storage& ss, socklen_t& sa_len);
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index acc1e4f..787a149 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -47,7 +47,6 @@
REGISTER(register_java_lang_StringToReal);
REGISTER(register_java_lang_System);
REGISTER(register_java_math_NativeBN);
- REGISTER(register_java_nio_ByteOrder);
REGISTER(register_java_text_Bidi);
REGISTER(register_java_util_jar_StrictJarFile);
REGISTER(register_java_util_regex_Matcher);
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index a11ea76..3cc9b06 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -35,6 +35,7 @@
#endif
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/un.h>
#include <sys/wait.h>
#include <sys/xattr.h>
#include <unistd.h>
@@ -211,6 +212,7 @@
initConstant(env, c, "ETIME", ETIME);
initConstant(env, c, "ETIMEDOUT", ETIMEDOUT);
initConstant(env, c, "ETXTBSY", ETXTBSY);
+ initConstant(env, c, "EUSERS", EUSERS);
#if EWOULDBLOCK != EAGAIN
#error EWOULDBLOCK != EAGAIN
#endif
@@ -550,6 +552,9 @@
initConstant(env, c, "S_IXOTH", S_IXOTH);
initConstant(env, c, "S_IXUSR", S_IXUSR);
initConstant(env, c, "TCP_NODELAY", TCP_NODELAY);
+ initConstant(env, c, "TIOCOUTQ", TIOCOUTQ);
+ // 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);
initConstant(env, c, "WEXITED", WEXITED);
initConstant(env, c, "WNOHANG", WNOHANG);
diff --git a/luni/src/main/native/java_lang_StrictMath.cpp b/luni/src/main/native/java_lang_StrictMath.cpp
index e8c6dfb..d0bb8ce 100644
--- a/luni/src/main/native/java_lang_StrictMath.cpp
+++ b/luni/src/main/native/java_lang_StrictMath.cpp
@@ -42,14 +42,6 @@
return ieee_remainder(a, b);
}
-static jdouble StrictMath_floor(JNIEnv*, jclass, jdouble a) {
- return ieee_floor(a);
-}
-
-static jdouble StrictMath_ceil(JNIEnv*, jclass, jdouble a) {
- return ieee_ceil(a);
-}
-
static jdouble StrictMath_rint(JNIEnv*, jclass, jdouble a) {
return ieee_rint(a);
}
@@ -68,9 +60,7 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(StrictMath, IEEEremainder, "!(DD)D"),
- NATIVE_METHOD(StrictMath, ceil, "!(D)D"),
NATIVE_METHOD(StrictMath, cos, "!(D)D"),
- NATIVE_METHOD(StrictMath, floor, "!(D)D"),
NATIVE_METHOD(StrictMath, hypot, "!(DD)D"),
NATIVE_METHOD(StrictMath, nextafter, "!(DD)D"),
NATIVE_METHOD(StrictMath, pow, "!(DD)D"),
diff --git a/luni/src/main/native/java_nio_ByteOrder.cpp b/luni/src/main/native/java_nio_ByteOrder.cpp
deleted file mode 100644
index 3269bc2..0000000
--- a/luni/src/main/native/java_nio_ByteOrder.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#define LOG_TAG "ByteOrder"
-
-#include "JNIHelp.h"
-#include "JniConstants.h"
-
-static jboolean ByteOrder_isLittleEndian(JNIEnv*, jclass) {
- int i = 1;
- return *reinterpret_cast<jbyte*>(&i) == 1;
-}
-
-static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(ByteOrder, isLittleEndian, "!()Z"),
-};
-void register_java_nio_ByteOrder(JNIEnv* env) {
- jniRegisterNativeMethods(env, "java/nio/ByteOrder", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/java_util_jar_StrictJarFile.cpp b/luni/src/main/native/java_util_jar_StrictJarFile.cpp
index 03d0784..09dc5a0 100644
--- a/luni/src/main/native/java_util_jar_StrictJarFile.cpp
+++ b/luni/src/main/native/java_util_jar_StrictJarFile.cpp
@@ -98,11 +98,11 @@
int32_t error = 0;
if (prefixChars.size() == 0) {
error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
- handle->CookieAddress(), NULL);
+ handle->CookieAddress(), NULL, NULL);
} else {
- ZipEntryName entry_name(prefixChars.c_str());
+ ZipString entry_name(prefixChars.c_str());
error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
- handle->CookieAddress(), &entry_name);
+ handle->CookieAddress(), &entry_name, NULL);
}
if (error) {
@@ -115,7 +115,7 @@
static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) {
ZipEntry data;
- ZipEntryName entryName;
+ ZipString entryName;
IterationHandle* handle = reinterpret_cast<IterationHandle*>(iterationHandle);
const int32_t error = Next(*handle->CookieAddress(), &data, &entryName);
@@ -141,7 +141,7 @@
ZipEntry data;
const int32_t error = FindEntry(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
- ZipEntryName(entryNameChars.c_str()), &data);
+ ZipString(entryNameChars.c_str()), &data);
if (error) {
return NULL;
}
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 99b76f9..1f2f008 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -57,6 +57,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
+#include <sys/un.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <sys/xattr.h>
@@ -317,8 +318,40 @@
std::vector<ScopedT*> mScopedBuffers;
};
-static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss) {
- if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6 || ss.ss_family == AF_UNIX) {
+/**
+ * Returns a jbyteArray containing the sockaddr_un.sun_path from ss. As per unix(7) sa_len should be
+ * the length of ss as returned by getsockname(2), getpeername(2), or accept(2).
+ * If the returned array is of length 0 the sockaddr_un refers to an unnamed socket.
+ * A null pointer is returned in the event of an error. See unix(7) for more information.
+ */
+static jbyteArray getUnixSocketPath(JNIEnv* env, const sockaddr_storage& ss,
+ const socklen_t& sa_len) {
+ if (ss.ss_family != AF_UNIX) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "getUnixSocketPath unsupported ss_family: %i", ss.ss_family);
+ return NULL;
+ }
+
+ const struct sockaddr_un* un_addr = reinterpret_cast<const struct sockaddr_un*>(&ss);
+ // The length of sun_path is sa_len minus the length of the overhead (ss_family).
+ // See unix(7) for details. This calculation must match that of socket_make_sockaddr_un() in
+ // socket_local_client.c and javaUnixSocketAddressToSockaddr() to interoperate.
+ size_t pathLength = sa_len - offsetof(struct sockaddr_un, sun_path);
+
+ jbyteArray javaSunPath = env->NewByteArray(pathLength);
+ if (javaSunPath == NULL) {
+ return NULL;
+ }
+
+ if (pathLength > 0) {
+ env->SetByteArrayRegion(javaSunPath, 0, pathLength,
+ reinterpret_cast<const jbyte*>(&un_addr->sun_path));
+ }
+ return javaSunPath;
+}
+
+static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss, const socklen_t sa_len) {
+ if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) {
jint port;
jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
if (inetAddress == NULL) {
@@ -327,6 +360,15 @@
static jmethodID ctor = env->GetMethodID(JniConstants::inetSocketAddressClass,
"<init>", "(Ljava/net/InetAddress;I)V");
return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port);
+ } else if (ss.ss_family == AF_UNIX) {
+ static jmethodID ctor = env->GetMethodID(JniConstants::unixSocketAddressClass,
+ "<init>", "([B)V");
+
+ jbyteArray javaSunPath = getUnixSocketPath(env, ss, sa_len);
+ if (!javaSunPath) {
+ return NULL;
+ }
+ return env->NewObject(JniConstants::unixSocketAddressClass, ctor, javaSunPath);
} else if (ss.ss_family == AF_NETLINK) {
const struct sockaddr_nl* nl_addr = reinterpret_cast<const struct sockaddr_nl*>(&ss);
static jmethodID ctor = env->GetMethodID(JniConstants::netlinkSocketAddressClass,
@@ -448,8 +490,25 @@
return true;
}
-static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAddress, const sockaddr_storage& ss) {
- if (rc == -1 || javaInetSocketAddress == NULL) {
+static bool fillUnixSocketAddress(JNIEnv* env, jobject javaUnixSocketAddress,
+ const sockaddr_storage& ss, const socklen_t& sa_len) {
+ if (javaUnixSocketAddress == NULL) {
+ return true;
+ }
+ jbyteArray javaSunPath = getUnixSocketPath(env, ss, sa_len);
+ if (!javaSunPath) {
+ return false;
+ }
+
+ static jfieldID sunPathFid =
+ env->GetFieldID(JniConstants::unixSocketAddressClass, "sun_path", "[B");
+ env->SetObjectField(javaUnixSocketAddress, sunPathFid, javaSunPath);
+ return true;
+}
+
+static bool fillInetSocketAddress(JNIEnv* env, jobject javaInetSocketAddress,
+ const sockaddr_storage& ss) {
+ if (javaInetSocketAddress == NULL) {
return true;
}
// Fill out the passed-in InetSocketAddress with the sender's IP address and port number.
@@ -465,6 +524,23 @@
return true;
}
+static bool fillSocketAddress(JNIEnv* env, jobject javaSocketAddress, const sockaddr_storage& ss,
+ const socklen_t& sa_len) {
+ if (javaSocketAddress == NULL) {
+ return true;
+ }
+
+ if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) {
+ return fillInetSocketAddress(env, javaSocketAddress, ss);
+ } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::unixSocketAddressClass)) {
+ return fillUnixSocketAddress(env, javaSocketAddress, ss, sa_len);
+ }
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ "unsupported SocketAddress subclass");
+ return false;
+
+}
+
static void javaInetSocketAddressToInetAddressAndPort(
JNIEnv* env, jobject javaInetSocketAddress, jobject& javaInetAddress, jint& port) {
static jfieldID addressFid = env->GetFieldID(
@@ -497,6 +573,31 @@
return true;
}
+static bool javaUnixSocketAddressToSockaddr(
+ JNIEnv* env, jobject javaUnixSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+ static jfieldID sunPathFid = env->GetFieldID(
+ JniConstants::unixSocketAddressClass, "sun_path", "[B");
+
+ struct sockaddr_un* un_addr = reinterpret_cast<struct sockaddr_un*>(&ss);
+ memset (un_addr, 0, sizeof(sockaddr_un));
+ un_addr->sun_family = AF_UNIX;
+
+ jbyteArray javaSunPath = (jbyteArray) env->GetObjectField(javaUnixSocketAddress, sunPathFid);
+ jsize pathLength = env->GetArrayLength(javaSunPath);
+ if ((size_t) pathLength > sizeof(sockaddr_un::sun_path)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "sun_path too long: max=%i, is=%i",
+ sizeof(sockaddr_un::sun_path), pathLength);
+ return false;
+ }
+ env->GetByteArrayRegion(javaSunPath, 0, pathLength, (jbyte*) un_addr->sun_path);
+ // sa_len is sun_path plus the length of the overhead (ss_family_t). See unix(7) for
+ // details. This calculation must match that of socket_make_sockaddr_un() in
+ // socket_local_client.c and getUnixSocketPath() to interoperate.
+ sa_len = offsetof(struct sockaddr_un, sun_path) + pathLength;
+ return true;
+}
+
static bool javaPacketSocketAddressToSockaddr(
JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
static jfieldID protocolFid = env->GetFieldID(
@@ -546,6 +647,8 @@
return javaInetSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
} else if (env->IsInstanceOf(javaSocketAddress, JniConstants::packetSocketAddressClass)) {
return javaPacketSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+ } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::unixSocketAddressClass)) {
+ return javaUnixSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
}
jniThrowException(env, "java/lang/UnsupportedOperationException",
"unsupported SocketAddress subclass");
@@ -579,7 +682,7 @@
throwErrnoException(env, is_sockname ? "getsockname" : "getpeername");
return NULL;
}
- return makeSocketAddress(env, ss);
+ return makeSocketAddress(env, ss, byteCount);
}
class Passwd {
@@ -618,14 +721,14 @@
struct passwd* mResult;
};
-static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaInetSocketAddress) {
+static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
sockaddr_storage ss;
socklen_t sl = sizeof(ss);
memset(&ss, 0, sizeof(ss));
- sockaddr* peer = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
- socklen_t* peerLength = (javaInetSocketAddress != NULL) ? &sl : 0;
+ sockaddr* peer = (javaSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+ socklen_t* peerLength = (javaSocketAddress != NULL) ? &sl : 0;
jint clientFd = NET_FAILURE_RETRY(env, int, accept, javaFd, peer, peerLength);
- if (clientFd == -1 || !fillInetSocketAddress(env, clientFd, javaInetSocketAddress, ss)) {
+ if (clientFd == -1 || !fillSocketAddress(env, javaSocketAddress, ss, *peerLength)) {
close(clientFd);
return NULL;
}
@@ -1435,7 +1538,10 @@
sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0;
jint recvCount = NET_FAILURE_RETRY(env, ssize_t, recvfrom, javaFd, bytes.get() + byteOffset, byteCount, flags, from, fromLength);
- fillInetSocketAddress(env, recvCount, javaInetSocketAddress, ss);
+ if (recvCount == -1) {
+ return recvCount;
+ }
+ fillInetSocketAddress(env, javaInetSocketAddress, ss);
return recvCount;
}
@@ -1818,6 +1924,14 @@
return makeStructUtsname(env, buf);
}
+static void Posix_unlink(JNIEnv* env, jobject, jstring javaPathname) {
+ ScopedUtfChars pathname(env, javaPathname);
+ if (pathname.c_str() == NULL) {
+ return;
+ }
+ throwIfMinusOne(env, "unlink", unlink(pathname.c_str()));
+}
+
static void Posix_unsetenv(JNIEnv* env, jobject, jstring javaName) {
ScopedUtfChars name(env, javaName);
if (name.c_str() == NULL) {
@@ -1856,7 +1970,7 @@
{ #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName ## variant) }
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/InetSocketAddress;)Ljava/io/FileDescriptor;"),
+ NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
NATIVE_METHOD(Posix, android_getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;"),
NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
@@ -1970,6 +2084,7 @@
NATIVE_METHOD(Posix, tcsendbreak, "(Ljava/io/FileDescriptor;I)V"),
NATIVE_METHOD(Posix, umaskImpl, "(I)I"),
NATIVE_METHOD(Posix, uname, "()Landroid/system/StructUtsname;"),
+ NATIVE_METHOD(Posix, unlink, "(Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, unsetenv, "(Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, waitpid, "(ILandroid/util/MutableInt;I)I"),
NATIVE_METHOD(Posix, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),
diff --git a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index 48defc1..f6f812c 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -31,7 +31,7 @@
#include <memory>
#include <string.h>
-#include <libexpat/expat.h>
+#include <expat.h>
#define BUCKET_COUNT 128
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index 73ed7cb..78ec37a 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -27,7 +27,6 @@
java_lang_StringToReal.cpp \
java_lang_System.cpp \
java_math_NativeBN.cpp \
- java_nio_ByteOrder.cpp \
java_text_Bidi.cpp \
java_util_jar_StrictJarFile.cpp \
java_util_regex_Matcher.cpp \
diff --git a/luni/src/test/java/android/system/UnixSocketAddressTest.java b/luni/src/test/java/android/system/UnixSocketAddressTest.java
new file mode 100644
index 0000000..f1b7fc2
--- /dev/null
+++ b/luni/src/test/java/android/system/UnixSocketAddressTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 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 android.system;
+
+import junit.framework.TestCase;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+public class UnixSocketAddressTest extends TestCase {
+
+ public void testFilesystemSunPath() throws Exception {
+ String path = "/foo/bar";
+ UnixSocketAddress sa = UnixSocketAddress.createFileSystem(path);
+
+ byte[] abstractNameBytes = path.getBytes(StandardCharsets.UTF_8);
+ byte[] expected = new byte[abstractNameBytes.length + 1];
+ // See unix(7)
+ System.arraycopy(abstractNameBytes, 0, expected, 0, abstractNameBytes.length);
+ assertTrue(Arrays.equals(expected, sa.getSunPath()));
+ }
+
+ public void testUnnamedSunPath() throws Exception {
+ UnixSocketAddress sa = UnixSocketAddress.createUnnamed();
+ assertEquals(0, sa.getSunPath().length);
+ }
+
+ public void testAbstractSunPath() throws Exception {
+ String abstractName = "abstract";
+ UnixSocketAddress sa = UnixSocketAddress.createAbstract(abstractName);
+ byte[] abstractNameBytes = abstractName.getBytes(StandardCharsets.UTF_8);
+ byte[] expected = new byte[abstractNameBytes.length + 1];
+ // See unix(7)
+ System.arraycopy(abstractNameBytes, 0, expected, 1, abstractNameBytes.length);
+ assertTrue(Arrays.equals(expected, sa.getSunPath()));
+ }
+}
diff --git a/luni/src/test/java/dalvik/system/JniTest.java b/luni/src/test/java/dalvik/system/JniTest.java
index 37e687d..19f820e 100644
--- a/luni/src/test/java/dalvik/system/JniTest.java
+++ b/luni/src/test/java/dalvik/system/JniTest.java
@@ -361,8 +361,6 @@
assertEquals(Object.class, envGetSuperclass(String.class));
assertEquals(null, envGetSuperclass(Object.class));
assertEquals(null, envGetSuperclass(int.class));
-
- // incorrect! the spec says this should be null. http://b/5652725
- assertEquals(Object.class, envGetSuperclass(Runnable.class));
+ assertEquals(null, envGetSuperclass(Runnable.class));
}
}
diff --git a/luni/src/test/java/libcore/io/ClassPathURLStreamHandlerTest.java b/luni/src/test/java/libcore/io/ClassPathURLStreamHandlerTest.java
new file mode 100644
index 0000000..eb66c3a
--- /dev/null
+++ b/luni/src/test/java/libcore/io/ClassPathURLStreamHandlerTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2015 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.io;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.net.UnknownServiceException;
+import java.util.Arrays;
+
+import tests.support.resource.Support_Resources;
+
+public class ClassPathURLStreamHandlerTest extends TestCase {
+
+ // A well formed jar file with 6 entries.
+ private static final String JAR = "ClassPathURLStreamHandlerTest.jar";
+ private static final String ENTRY_IN_ROOT = "root.txt";
+ private static final String ENTRY_IN_SUBDIR = "foo/bar/baz.txt";
+ private static final String ENTRY_STORED = "stored_file.txt";
+ private static final String ENTRY_WITH_SPACES_ENCODED = "file%20with%20spaces.txt";
+ private static final String ENTRY_WITH_SPACES_UNENCODED = "file with spaces.txt";
+ private static final String ENTRY_THAT_NEEDS_ESCAPING = "file_with_percent20_%20.txt";
+ private static final String ENTRY_WITH_RELATIVE_PATH = "foo/../foo/bar/baz.txt";
+ private static final String MISSING_ENTRY = "Wrong.resource";
+
+ private File jarFile;
+
+ @Override
+ protected void setUp() {
+ File resources = Support_Resources.createTempFolder();
+ Support_Resources.copyFile(resources, null, JAR);
+ jarFile = new File(resources, JAR);
+ }
+
+ public void testConstructor() throws Exception {
+ try {
+ ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler("Missing.file");
+ fail("Should throw IOException");
+ } catch (IOException expected) {
+ }
+
+ String fileName = jarFile.getCanonicalPath();
+ ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler(fileName);
+ streamHandler.close();
+ }
+
+ public void testGetEntryOrNull() throws Exception {
+ String fileName = jarFile.getCanonicalPath();
+ ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler(fileName);
+
+ assertNotNull(streamHandler.getEntryUrlOrNull(ENTRY_IN_ROOT));
+ assertNotNull(streamHandler.getEntryUrlOrNull(ENTRY_IN_SUBDIR));
+ assertNotNull(streamHandler.getEntryUrlOrNull(ENTRY_WITH_SPACES_UNENCODED));
+ assertNotNull(streamHandler.getEntryUrlOrNull(ENTRY_THAT_NEEDS_ESCAPING));
+
+ // getEntryOrNull() performs an exact match on the entry name.
+ assertNull(streamHandler.getEntryUrlOrNull(MISSING_ENTRY));
+ assertNull(streamHandler.getEntryUrlOrNull("/" + ENTRY_IN_ROOT));
+ assertNull(streamHandler.getEntryUrlOrNull("/" + ENTRY_IN_SUBDIR));
+ assertNull(streamHandler.getEntryUrlOrNull(ENTRY_WITH_SPACES_ENCODED));
+ assertNull(streamHandler.getEntryUrlOrNull(ENTRY_WITH_RELATIVE_PATH));
+ streamHandler.close();
+ }
+
+ public void testIsEntryStored() throws IOException {
+ String fileName = jarFile.getCanonicalPath();
+ ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler(fileName);
+
+ assertFalse(streamHandler.isEntryStored("this/file/does/not/exist.txt"));
+ // This one is compressed
+ assertFalse(streamHandler.isEntryStored(ENTRY_IN_SUBDIR));
+ assertTrue(streamHandler.isEntryStored(ENTRY_STORED));
+ }
+
+ public void testOpenConnection() throws Exception {
+ String fileName = jarFile.getCanonicalPath();
+ ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler(fileName);
+
+ assertOpenConnectionOk(jarFile, ENTRY_IN_ROOT, streamHandler);
+ assertOpenConnectionOk(jarFile, ENTRY_IN_SUBDIR, streamHandler);
+ assertOpenConnectionOk(jarFile, ENTRY_WITH_SPACES_ENCODED, streamHandler);
+ assertOpenConnectionOk(jarFile, ENTRY_WITH_SPACES_UNENCODED, streamHandler);
+
+ assertOpenConnectionConnectFails(jarFile, ENTRY_WITH_RELATIVE_PATH, streamHandler);
+ assertOpenConnectionConnectFails(jarFile, MISSING_ENTRY, streamHandler);
+ assertOpenConnectionConnectFails(jarFile, ENTRY_THAT_NEEDS_ESCAPING, streamHandler);
+
+ streamHandler.close();
+ }
+
+ private void assertOpenConnectionConnectFails(
+ File jarFile, String entryName, URLStreamHandler streamHandler) throws IOException {
+
+ URL standardUrl = createJarUrl(jarFile, entryName, null /* use default stream handler */);
+ try {
+ standardUrl.openConnection().connect();
+ fail();
+ } catch (FileNotFoundException expected) {
+ }
+
+ URL actualUrl = createJarUrl(jarFile, entryName, streamHandler);
+ try {
+ actualUrl.openConnection().connect();
+ fail();
+ } catch (FileNotFoundException expected) {
+ }
+ }
+
+ private static void assertOpenConnectionOk(File jarFile, String entryName,
+ ClassPathURLStreamHandler streamHandler) throws IOException {
+ URL standardUrl = createJarUrl(jarFile, entryName, null /* use default stream handler */);
+ URLConnection standardUrlConnection = standardUrl.openConnection();
+ assertNotNull(standardUrlConnection);
+
+ URL actualUrl = createJarUrl(jarFile, entryName, streamHandler);
+ URLConnection actualUrlConnection = actualUrl.openConnection();
+ assertNotNull(actualUrlConnection);
+ assertBehaviorSame(standardUrlConnection, actualUrlConnection);
+ }
+
+ private static void assertBehaviorSame(URLConnection standardURLConnection,
+ URLConnection actualUrlConnection) throws IOException {
+
+ JarURLConnection standardJarUrlConnection = (JarURLConnection) standardURLConnection;
+ JarURLConnection actualJarUrlConnection = (JarURLConnection) actualUrlConnection;
+
+ byte[] actualBytes = Streams.readFully(actualJarUrlConnection.getInputStream());
+ byte[] standardBytes = Streams.readFully(standardJarUrlConnection.getInputStream());
+ assertEquals(Arrays.toString(standardBytes), Arrays.toString(actualBytes));
+
+ try {
+ actualJarUrlConnection.getOutputStream();
+ fail();
+ } catch (UnknownServiceException expected) {
+ }
+
+ assertEquals(
+ standardJarUrlConnection.getJarFile().getName(),
+ actualJarUrlConnection.getJarFile().getName());
+
+ assertEquals(
+ standardJarUrlConnection.getJarEntry().getName(),
+ actualJarUrlConnection.getJarEntry().getName());
+
+ assertEquals(
+ standardJarUrlConnection.getJarFileURL(),
+ actualJarUrlConnection.getJarFileURL());
+
+ assertEquals(
+ standardJarUrlConnection.getContentType(),
+ actualJarUrlConnection.getContentType());
+
+ assertEquals(
+ standardJarUrlConnection.getContentLength(),
+ actualJarUrlConnection.getContentLength());
+ }
+
+ private static URL createJarUrl(File jarFile, String entryName, URLStreamHandler streamHandler)
+ throws MalformedURLException {
+ return new URL("jar", null, -1, jarFile.toURI() + "!/" + entryName, streamHandler);
+ }
+
+}
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index 9b38ee9..f1d6668 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -22,6 +22,8 @@
import android.system.PacketSocketAddress;
import android.system.StructTimeval;
import android.system.StructUcred;
+import android.system.UnixSocketAddress;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -30,10 +32,8 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
-import java.net.InetUnixAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
-import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -70,19 +70,25 @@
public void testUnixDomainSockets_in_file_system() throws Exception {
String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
new File(path).delete();
- checkUnixDomainSocket(new InetUnixAddress(path), false);
+ checkUnixDomainSocket(UnixSocketAddress.createFileSystem(path), false);
}
public void testUnixDomainSocket_abstract_name() throws Exception {
// Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
- byte[] path = "/abstract_name_unix_socket".getBytes("UTF-8");
- path[0] = 0;
- checkUnixDomainSocket(new InetUnixAddress(path), true);
+ checkUnixDomainSocket(UnixSocketAddress.createAbstract("/abstract_name_unix_socket"), true);
}
- private void checkUnixDomainSocket(final InetUnixAddress address, final boolean isAbstract) throws Exception {
+ public void testUnixDomainSocket_unnamed() throws Exception {
+ final FileDescriptor fd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+ // unix(7) says an unbound socket is unnamed.
+ checkNoSockName(fd);
+ Libcore.os.close(fd);
+ }
+
+ private void checkUnixDomainSocket(final UnixSocketAddress address, final boolean isAbstract)
+ throws Exception {
final FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
- Libcore.os.bind(serverFd, address, 0);
+ Libcore.os.bind(serverFd, address);
Libcore.os.listen(serverFd, 5);
checkSockName(serverFd, isAbstract, address);
@@ -90,7 +96,7 @@
Thread server = new Thread(new Runnable() {
public void run() {
try {
- InetSocketAddress peerAddress = new InetSocketAddress();
+ UnixSocketAddress peerAddress = UnixSocketAddress.createUnnamed();
FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
checkSockName(clientFd, isAbstract, address);
checkNoName(peerAddress);
@@ -119,7 +125,7 @@
FileDescriptor clientFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
- Libcore.os.connect(clientFd, address, 0);
+ Libcore.os.connect(clientFd, address);
checkNoSockName(clientFd);
String string = "hello, world!";
@@ -135,26 +141,25 @@
Libcore.os.close(clientFd);
}
- private void checkSockName(FileDescriptor fd, boolean isAbstract, InetAddress address) throws Exception {
- InetSocketAddress isa = (InetSocketAddress) Libcore.os.getsockname(fd);
+ private static void checkSockName(FileDescriptor fd, boolean isAbstract,
+ UnixSocketAddress address) throws Exception {
+ UnixSocketAddress isa = (UnixSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals(address, isa);
if (isAbstract) {
- checkNoName(isa);
- } else {
- assertEquals(address, isa.getAddress());
+ assertEquals(0, isa.getSunPath()[0]);
}
}
- private void checkNoName(SocketAddress sa) {
- InetSocketAddress isa = (InetSocketAddress) sa;
- assertEquals(0, isa.getAddress().getAddress().length);
+ private void checkNoName(UnixSocketAddress usa) {
+ assertEquals(0, usa.getSunPath().length);
}
private void checkNoPeerName(FileDescriptor fd) throws Exception {
- checkNoName(Libcore.os.getpeername(fd));
+ checkNoName((UnixSocketAddress) Libcore.os.getpeername(fd));
}
private void checkNoSockName(FileDescriptor fd) throws Exception {
- checkNoName(Libcore.os.getsockname(fd));
+ checkNoName((UnixSocketAddress) Libcore.os.getsockname(fd));
}
public void test_strsignal() throws Exception {
@@ -220,7 +225,7 @@
}
static void checkByteBufferPositions_sendto_recvfrom(
- int family, InetAddress loopback) throws Exception {
+ int family, InetAddress loopback) throws Exception {
final FileDescriptor serverFd = Libcore.os.socket(family, SOCK_STREAM, 0);
Libcore.os.bind(serverFd, loopback, 0);
Libcore.os.listen(serverFd, 5);
@@ -253,13 +258,13 @@
}
});
-
server.start();
FileDescriptor clientFd = Libcore.os.socket(family, SOCK_STREAM, 0);
Libcore.os.connect(clientFd, address.getAddress(), address.getPort());
- final byte[] bytes = "good bye, cruel black hole with fancy distortion".getBytes(StandardCharsets.US_ASCII);
+ final byte[] bytes = "good bye, cruel black hole with fancy distortion"
+ .getBytes(StandardCharsets.US_ASCII);
assertTrue(bytes.length > 24);
ByteBuffer input = ByteBuffer.wrap(bytes);
@@ -341,11 +346,11 @@
}
public void test_sendtoSocketAddress_af_inet() throws Exception {
- checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
+ checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
}
public void test_sendtoSocketAddress_af_inet6() throws Exception {
- checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
+ checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
}
public void test_socketFamilies() throws Exception {
@@ -373,11 +378,11 @@
private static void assertArrayEquals(byte[] expected, byte[] actual) {
assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
- Arrays.equals(expected, actual));
+ Arrays.equals(expected, actual));
}
private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
- byte type, byte responseType, boolean useSendto) throws Exception {
+ byte type, byte responseType, boolean useSendto) throws Exception {
int len = packet.length;
packet[0] = type;
if (useSendto) {
@@ -407,8 +412,8 @@
final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
final byte[] packet = ("\000\000\000\000" + // ICMP type, code.
- "\000\000\000\003" + // ICMP ID (== port), sequence number.
- "Hello myself").getBytes(StandardCharsets.US_ASCII);
+ "\000\000\000\003" + // ICMP ID (== port), sequence number.
+ "Hello myself").getBytes(StandardCharsets.US_ASCII);
FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
InetAddress ipv6Loopback = InetAddress.getByName("::1");
@@ -421,58 +426,72 @@
checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
}
- private static void assertPartial(byte[] expected, byte[] actual) {
- for (int i = 0; i < expected.length; i++) {
- if (expected[i] != actual[i]) {
- fail("Expected " + Arrays.toString(expected) + " but found "
- + Arrays.toString(actual));
- }
- }
+ public void test_unlink() throws Exception {
+ File f = File.createTempFile("OsTest", "tst");
+ assertTrue(f.exists());
+ Libcore.os.unlink(f.getAbsolutePath());
+ assertFalse(f.exists());
+
+ try {
+ Libcore.os.unlink(f.getAbsolutePath());
+ fail();
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.ENOENT, e.errno);
}
+ }
- public void test_xattr() throws Exception {
- final String NAME_TEST = "user.meow";
+ public void test_xattr() throws Exception {
+ final String NAME_TEST = "user.meow";
- final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
- final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
+ final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+ final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
- File file = File.createTempFile("xattr", "test");
- String path = file.getAbsolutePath();
+ File file = File.createTempFile("xattr", "test");
+ String path = file.getAbsolutePath();
- byte[] tmp = new byte[1024];
- try {
- try {
- Libcore.os.getxattr(path, NAME_TEST, tmp);
- fail("Expected ENODATA");
- } catch (ErrnoException e) {
- assertEquals(OsConstants.ENODATA, e.errno);
- }
+ byte[] tmp = new byte[1024];
+ try {
+ try {
+ Libcore.os.getxattr(path, NAME_TEST, tmp);
+ fail("Expected ENODATA");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.ENODATA, e.errno);
+ }
- Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
- assertEquals(VALUE_CAKE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
- assertPartial(VALUE_CAKE, tmp);
+ Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+ assertEquals(VALUE_CAKE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
+ assertStartsWith(VALUE_CAKE, tmp);
- try {
- Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
- fail("Expected EEXIST");
- } catch (ErrnoException e) {
- assertEquals(OsConstants.EEXIST, e.errno);
- }
+ try {
+ Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
+ fail("Expected EEXIST");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.EEXIST, e.errno);
+ }
- Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
- assertEquals(VALUE_PIE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
- assertPartial(VALUE_PIE, tmp);
+ Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
+ assertEquals(VALUE_PIE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
+ assertStartsWith(VALUE_PIE, tmp);
- Libcore.os.removexattr(path, NAME_TEST);
- try {
- Libcore.os.getxattr(path, NAME_TEST, tmp);
- fail("Expected ENODATA");
- } catch (ErrnoException e) {
- assertEquals(OsConstants.ENODATA, e.errno);
- }
+ Libcore.os.removexattr(path, NAME_TEST);
+ try {
+ Libcore.os.getxattr(path, NAME_TEST, tmp);
+ fail("Expected ENODATA");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.ENODATA, e.errno);
+ }
- } finally {
- file.delete();
- }
+ } finally {
+ file.delete();
}
+ }
+
+ private static void assertStartsWith(byte[] expectedContents, byte[] container) {
+ for (int i = 0; i < expectedContents.length; i++) {
+ if (expectedContents[i] != container[i]) {
+ fail("Expected " + Arrays.toString(expectedContents) + " but found "
+ + Arrays.toString(expectedContents));
+ }
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/charset/CharsetDecoderTest.java b/luni/src/test/java/libcore/java/nio/charset/CharsetDecoderTest.java
index e874a1b..4fa6e0c 100644
--- a/luni/src/test/java/libcore/java/nio/charset/CharsetDecoderTest.java
+++ b/luni/src/test/java/libcore/java/nio/charset/CharsetDecoderTest.java
@@ -105,4 +105,15 @@
assertEquals(1, cb.position());
assertEquals('\u2603', cb.get(0));
}
+
+ public void testBufferWithNonZeroOffset() {
+ CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
+ CharBuffer cb = CharBuffer.allocate(128);
+ cb.position(42);
+ CharBuffer out = cb.slice();
+ CoderResult cr = decoder.decode(
+ ByteBuffer.wrap(new byte[] { 'h', 'e', 'l', 'l', 'o'}), out, false);
+ assertTrue(cr.isUnderflow());
+ assertEquals(5, out.position());
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java
index 9005f25..7d7fde1 100644
--- a/luni/src/test/java/libcore/java/util/LocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/LocaleTest.java
@@ -791,7 +791,7 @@
public void test_setLanguageTag_malformedTags() {
Locale l = fromLanguageTag("a", false);
- assertEquals("und", l.getLanguage());
+ assertEquals("", l.getLanguage());
assertEquals("", l.getCountry());
assertEquals("", l.getVariant());
assertEquals("", l.getScript());
@@ -994,8 +994,7 @@
// Empty builder.
Locale l = b.build();
- // TODO: Fix this. We should return "und" and not NULL.
- // assertEquals("und", l.toLanguageTag());
+ assertEquals("und", l.toLanguageTag());
// Only language.
b = new Locale.Builder();
@@ -1134,7 +1133,7 @@
// Irregular grandfathered locale.
Locale enochian = Locale.forLanguageTag("i-enochian");
- assertEquals("und", enochian.getLanguage());
+ assertEquals("", enochian.getLanguage());
assertEquals("i-enochian", enochian.getExtension(Locale.PRIVATE_USE_EXTENSION));
assertEquals("", enochian.getCountry());
assertEquals("", enochian.getScript());
@@ -1195,7 +1194,7 @@
System.setUnchangeableSystemProperty("user.locale", "dexx-Latn-DE");
l = Locale.getDefaultLocaleFromSystemProperties();
- assertEquals("und", l.getLanguage());
+ assertEquals("", l.getLanguage());
assertEquals("DE", l.getCountry());
} finally {
System.setUnchangeableSystemProperty("user.language", userLanguage);
diff --git a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
index 9496ad0..d44918c 100644
--- a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
+++ b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
@@ -50,6 +50,7 @@
Support_Resources.copyFile(resources, null, JAR_1);
String fileName = (new File(resources, JAR_1)).getCanonicalPath();
StrictJarFile jarFile = new StrictJarFile(fileName);
+ assertEquals(fileName, jarFile.getName());
jarFile.close();
}
@@ -173,6 +174,18 @@
assertThrowsOnInit("removed.jar");
}
+ public void testJarWithNoManifest() throws Exception {
+ String jarFileName = "StrictJarFileTestNoManifest.jar";
+ Support_Resources.copyFile(resources, null, jarFileName);
+ StrictJarFile jarFile = new StrictJarFile(
+ new File(resources, jarFileName).getAbsolutePath());
+
+ assertNull(jarFile.getManifest());
+ ZipEntry entry = jarFile.findEntry("Test.class");
+ assertNotNull(entry);
+ assertNull(jarFile.getCertificateChains(entry));
+ }
+
private void assertThrowsOnInit(String name) throws Exception {
Support_Resources.copyFile(resources, null, name);
try {
diff --git a/luni/src/test/java/libcore/java/util/prefs/OldAbstractPreferencesTest.java b/luni/src/test/java/libcore/java/util/prefs/OldAbstractPreferencesTest.java
index 693f0c2..0f8ed99 100644
--- a/luni/src/test/java/libcore/java/util/prefs/OldAbstractPreferencesTest.java
+++ b/luni/src/test/java/libcore/java/util/prefs/OldAbstractPreferencesTest.java
@@ -37,6 +37,12 @@
static final String nodeName = "mock";
+ /** Timeout used for Object.wait when no action is expected. */
+ static final long NO_ACTION_EXPECTED_TIMEOUT = 200;
+
+ /** Timeout used for Object.wait when an action is expected. */
+ static final long ACTION_EXPECTED_TIMEOUT = 0; // Wait indefinitely
+
private PreferencesFactory defaultFactory;
AbstractPreferences pref;
@@ -885,7 +891,9 @@
}
public synchronized void assertChanged(boolean expected) throws InterruptedException {
- wait(100);
+ if (!flagChange) {
+ wait(expected ? ACTION_EXPECTED_TIMEOUT : NO_ACTION_EXPECTED_TIMEOUT);
+ }
assertEquals(expected, flagChange);
flagChange = false;
}
@@ -933,12 +941,16 @@
}
public synchronized void assertAdded(boolean expected) throws InterruptedException {
- wait(100);
+ if (!flagAdded) {
+ wait(expected ? ACTION_EXPECTED_TIMEOUT : NO_ACTION_EXPECTED_TIMEOUT);
+ }
assertEquals(expected, flagAdded);
}
public synchronized void assertRemoved(boolean expected) throws InterruptedException {
- wait(100);
+ if (!flagRemoved) {
+ wait(expected ? ACTION_EXPECTED_TIMEOUT : NO_ACTION_EXPECTED_TIMEOUT);
+ }
assertEquals(expected, flagRemoved);
}
}
diff --git a/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java b/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
index 1b733f9..0986472 100644
--- a/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
@@ -16,10 +16,13 @@
package libcore.java.util.zip;
+import libcore.io.Streams;
+
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
@@ -43,6 +46,16 @@
assertTrue(entries.hasMoreElements());
ZipEntry ze = entries.nextElement();
assertEquals(2, ze.getSize());
+ InputStream is = null;
+ try {
+ is = zf.getInputStream(ze);
+ byte[] uncompressed = Streams.readFully(is);
+ assertEquals(2, uncompressed.length);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
} finally {
if (zf != null) {
zf.close();
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
index 49990a3..1dc22ca 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
@@ -18,6 +18,9 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
@@ -29,6 +32,7 @@
import java.util.Random;
import java.util.Set;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@@ -82,4 +86,33 @@
in.close();
}
}
+
+ // NOTE: Using octal because it's easiest to use "hexdump -b" to dump file contents.
+ private static final byte[] INCOMPLETE_ZIP = new byte[] {
+ 0120, 0113, 0003, 0004, 0024, 0000, 0010, 0010, 0010, 0000, 0002, 0035, (byte) 0330,
+ 0106, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0013,
+ 0000, 0000, 0000, 0146, 0157, 0157, 0057, 0142, 0141, 0162, 0056, 0160, 0156, 0147 };
+
+ // http://b//21846904
+ public void testReadOnIncompleteStream() throws Exception {
+ ZipInputStream zi = new ZipInputStream(new ByteArrayInputStream(INCOMPLETE_ZIP));
+ ZipEntry ze = zi.getNextEntry();
+
+ // read() and closeEntry() must throw IOExceptions to indicate that
+ // the stream is corrupt. The bug above reported that they would loop
+ // forever.
+ try {
+ zi.read(new byte[1024], 0, 1024);
+ fail();
+ } catch (IOException expected) {
+ }
+
+ try {
+ zi.closeEntry();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ zi.close();
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index 3c7ff0e..dce0e62 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -988,9 +988,20 @@
Security.addProvider(mockProviderInvalid);
try {
- Cipher.getInstance("FOO");
- fail("Should not find any matching providers");
- } catch (NoSuchAlgorithmException expected) {
+ Cipher c = Cipher.getInstance("FOO");
+ if (StandardNames.IS_RI) {
+ c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[16], "FOO"));
+ } else {
+ fail("Should not find any matching providers; found: " + c);
+ }
+ } catch (NoSuchAlgorithmException maybe) {
+ if (StandardNames.IS_RI) {
+ throw maybe;
+ }
+ } catch (ClassCastException maybe) {
+ if (!StandardNames.IS_RI) {
+ throw maybe;
+ }
} finally {
Security.removeProvider(mockProviderInvalid.getName());
}
@@ -2471,10 +2482,6 @@
(byte) 0x19, (byte) 0x35,
};
- private static final byte[][] AES_KEYS = new byte[][] {
- AES_128_KEY, AES_192_KEY, AES_256_KEY,
- };
-
private static final String[] AES_MODES = new String[] {
"AES/ECB",
"AES/CBC",
@@ -2590,7 +2597,42 @@
/*
* Test key generation:
* openssl rand -hex 16
- * echo 'ceaa31952dfd3d0f5af4b2042ba06094' | sed 's/\(..\)/(byte) 0x\1, /g'
+ * echo '787bdeecf05556eac5d3d865e435f6d9' | sed 's/\(..\)/(byte) 0x\1, /g'
+ */
+ private static final byte[] AES_192_CTR_NoPadding_TestVector_1_IV = new byte[] {
+ (byte) 0x78, (byte) 0x7b, (byte) 0xde, (byte) 0xec, (byte) 0xf0, (byte) 0x55,
+ (byte) 0x56, (byte) 0xea, (byte) 0xc5, (byte) 0xd3, (byte) 0xd8, (byte) 0x65,
+ (byte) 0xe4, (byte) 0x35, (byte) 0xf6, (byte) 0xd9,
+
+ };
+
+ /*
+ * Test vector generation:
+ * echo -n 'AES-192 is a silly option' | recode ../x1 | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] AES_192_CTR_NoPadding_TestVector_1_Plaintext = new byte[] {
+ (byte) 0x41, (byte) 0x45, (byte) 0x53, (byte) 0x2D, (byte) 0x31, (byte) 0x39,
+ (byte) 0x32, (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x61,
+ (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x6C, (byte) 0x6C, (byte) 0x79,
+ (byte) 0x20, (byte) 0x6F, (byte) 0x70, (byte) 0x74, (byte) 0x69, (byte) 0x6F,
+ (byte) 0x6E
+ };
+
+ /*
+ * Test vector generation:
+ * echo -n 'AES-192 is a silly option' | openssl enc -aes-192-ctr -K 5a7a3d7e40b64ed996f7afa15f97fd595e27db6af428e342 -iv 787bdeecf05556eac5d3d865e435f6d9 | recode ../x1 | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] AES_192_CTR_NoPadding_TestVector_1_Ciphertext = new byte[] {
+ (byte) 0xE9, (byte) 0xC6, (byte) 0xA0, (byte) 0x40, (byte) 0xC2, (byte) 0x6A,
+ (byte) 0xB5, (byte) 0x20, (byte) 0xFE, (byte) 0x9E, (byte) 0x65, (byte) 0xB7,
+ (byte) 0x7C, (byte) 0x5E, (byte) 0xFE, (byte) 0x1F, (byte) 0xF1, (byte) 0x6F,
+ (byte) 0x20, (byte) 0xAC, (byte) 0x37, (byte) 0xE9, (byte) 0x75, (byte) 0xE3,
+ (byte) 0x52
+ };
+
+ /*
+ * Test key generation: openssl rand -hex 16 echo
+ * 'ceaa31952dfd3d0f5af4b2042ba06094' | sed 's/\(..\)/(byte) 0x\1, /g'
*/
private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_IV = new byte[] {
(byte) 0xce, (byte) 0xaa, (byte) 0x31, (byte) 0x95, (byte) 0x2d, (byte) 0xfd,
@@ -2699,6 +2741,12 @@
AES_128_GCM_TestVector_1_Plaintext,
AES_128_GCM_TestVector_1_Encrypted));
if (IS_UNLIMITED) {
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CTR/NoPadding", AES_192_KEY,
+ AES_192_CTR_NoPadding_TestVector_1_IV,
+ null,
+ AES_192_CTR_NoPadding_TestVector_1_Plaintext,
+ AES_192_CTR_NoPadding_TestVector_1_Plaintext,
+ AES_192_CTR_NoPadding_TestVector_1_Ciphertext));
CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS5Padding", AES_256_KEY,
AES_256_CBC_PKCS5Padding_TestVector_1_IV,
null,
@@ -2789,7 +2837,8 @@
// empty decrypt
{
if (!isAEAD(p.transformation)
- && (StandardNames.IS_RI || provider.equals("AndroidOpenSSL"))) {
+ && (StandardNames.IS_RI || provider.equals("AndroidOpenSSL") ||
+ (provider.equals("BC") && p.transformation.contains("/CTR/")))) {
assertEquals(Arrays.toString(new byte[0]),
Arrays.toString(c.doFinal()));
@@ -2799,7 +2848,7 @@
} else if (provider.equals("BC") || isAEAD(p.transformation)) {
try {
c.doFinal();
- fail();
+ fail(p.transformation + " " + provider);
} catch (IllegalBlockSizeException maybe) {
if (isAEAD(p.transformation)) {
throw maybe;
@@ -2812,7 +2861,7 @@
try {
c.update(new byte[0]);
c.doFinal();
- fail();
+ fail(p.transformation + " " + provider);
} catch (IllegalBlockSizeException maybe) {
if (isAEAD(p.transformation)) {
throw maybe;
diff --git a/luni/src/test/java/libcore/xml/PullParserTest.java b/luni/src/test/java/libcore/xml/PullParserTest.java
index c59b358..b204c88 100644
--- a/luni/src/test/java/libcore/xml/PullParserTest.java
+++ b/luni/src/test/java/libcore/xml/PullParserTest.java
@@ -766,6 +766,16 @@
assertRelaxedParseFailure("<!DOCTYPE foo [<!ELEMENT foo EMPTY"); // EOF in read('>')
}
+ public void testWhitespacesAfterDOCTYPE() throws Exception {
+ XmlPullParser parser = newPullParser();
+ String test = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<!DOCTYPE root [\n" +
+ "<!ENTITY dummy \"dummy\">\n" +
+ "]> \n" +
+ "<root></root>";
+ assertParseSuccess(test, parser);
+ }
+
private void assertParseFailure(String xml) throws Exception {
XmlPullParser parser = newPullParser();
assertParseFailure(xml, parser);
@@ -787,6 +797,12 @@
}
}
+ private void assertParseSuccess(String xml, XmlPullParser parser) throws Exception {
+ parser.setInput(new StringReader(xml));
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ }
+ }
+
/**
* Creates a new pull parser.
*/
diff --git a/luni/src/test/resources/math_important_numbers.csv b/luni/src/test/resources/math_important_numbers.csv
index 3c71a4e..89a7525 100644
--- a/luni/src/test/resources/math_important_numbers.csv
+++ b/luni/src/test/resources/math_important_numbers.csv
@@ -630,6 +630,20 @@
ceil,-0x1.0p0,-0x1.0p0,-1.0
ceil,-0x0.0p0,-0x1.0p-1,-0.5
ceil,0x1.0p0,0x1.0p-1,0.5
+ceil,0x0.0p2047,0x0.0p2047,infty
+ceil,-0x0.0p2047,-0x0.0p2047,-infty
+ceil,0x1.0p2047,0x1.0p2047,NaN
+ceil,0x1.4c7eb53cccp39,0x1.4c7eb53ccab5fp39,7.140274745333553E11
+ceil,0x1.c8d8ep21,0x1.c8d8da6003191p21,3742491.296880909
+ceil,-0x1.5deeef088p35,-0x1.5deeef088909fp35,-4.696725715628246E10
+ceil,-0x1.bde332607a678p50,-0x1.bde332607a67bp50,-1.9610339084804148E15
+ceil,-0x0.0p0,-0x1.5162455ecd513p-117,-7.9318566885477E-36
+ceil,0x1.0p0,0x1.d71300e409ec1p-317,6.891928421440976E-96
+ceil,0x1.a12e8p17,0x1.a12e4b0df7367p17,213596.58636369856
+ceil,-0x1.5607af3b1eb1dp829,-0x1.5607af3b1eb1dp829,-4.7828929737254625E249
+ceil,0x1.9632259cc7e5ep51,0x1.9632259cc7e5ep51,3.572936802189103E15
+ceil,0x1.5a39cp20,0x1.5a39b156b4c84p20,1418139.083668501
+ceil,0x1.8c37dp21,0x1.8c37c99d61a3bp21,3245817.2018463886
ceil,-0x1.8p2,-0x1.921fb54442d18p2,-6.283185307179586
ceil,-0x1.8p2,-0x1.815e630c155e1p2,-6.021385919380436
ceil,-0x1.4p2,-0x1.709d10d3e7eabp2,-5.759586531581287
@@ -687,6 +701,20 @@
floor,-0x1.0p0,-0x1.0p0,-1.0
floor,-0x1.0p0,-0x1.0p-1,-0.5
floor,0x0.0p0,0x1.0p-1,0.5
+floor,0x0.0p2047,0x0.0p2047,infty
+floor,-0x0.0p2047,-0x0.0p2047,-infty
+floor,0x1.0p2047,0x1.0p2047,NaN
+floor,0x1.4c7eb53ccap39,0x1.4c7eb53ccab5fp39,7.140274745333553E11
+floor,0x1.c8d8d8p21,0x1.c8d8da6003191p21,3742491.296880909
+floor,-0x1.5deeef08ap35,-0x1.5deeef088909fp35,-4.696725715628246E10
+floor,-0x1.bde332607a67cp50,-0x1.bde332607a67bp50,-1.9610339084804148E15
+floor,0x0.0p0,0x1.ba884e7f0bc51p-184,7.049948629370372E-56
+floor,-0x1.0p0,-0x1.bc0b3abd1b587p-51,-7.702933170334643E-16
+floor,-0x1.0p1,-0x1.ff1fa889b752ep0,-1.99657681810579
+floor,-0x1.255b340ab85a3p784,-0x1.255b340ab85a3p784,-1.1659287182288336E236
+floor,0x1.d0783f3901842p51,0x1.d0783f3901842p51,4.085518816513057E15
+floor,-0x1.6e715p20,-0x1.6e71470cef763p20,-1500948.440658056
+floor,-0x1.5485cp24,-0x1.5485bfebe1e42p24,-2.2316479921415575E7
floor,-0x1.cp2,-0x1.921fb54442d18p2,-6.283185307179586
floor,-0x1.cp2,-0x1.815e630c155e1p2,-6.021385919380436
floor,-0x1.8p2,-0x1.709d10d3e7eabp2,-5.759586531581287
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index cfd519a..1ea6b06 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -311,25 +311,29 @@
// Only available with the SunPKCS11-NSS provider,
// which seems to be enabled in OpenJDK 6 but not Oracle Java 6
if (Security.getProvider("SunPKCS11-NSS") != null) {
- provide("AlgorithmParameters", "EC");
provide("Cipher", "AES/CBC/NOPADDING");
provide("Cipher", "DES/CBC/NOPADDING");
provide("Cipher", "DESEDE/CBC/NOPADDING");
provide("Cipher", "RSA/ECB/PKCS1PADDING");
provide("KeyAgreement", "DH");
- provide("KeyAgreement", "ECDH");
provide("KeyFactory", "DH");
- provide("KeyFactory", "EC");
provide("KeyPairGenerator", "DH");
- provide("KeyPairGenerator", "EC");
provide("KeyStore", "PKCS11");
provide("MessageDigest", "SHA1");
provide("SecretKeyFactory", "AES");
provide("SecretKeyFactory", "ARCFOUR");
provide("SecureRandom", "PKCS11");
provide("Signature", "DSA");
- provide("Signature", "NONEWITHECDSA");
provide("Signature", "RAWDSA");
+ }
+
+ if (Security.getProvider("SunPKCS11-NSS") != null ||
+ Security.getProvider("SunEC") != null) {
+ provide("AlgorithmParameters", "EC");
+ provide("KeyAgreement", "ECDH");
+ provide("KeyFactory", "EC");
+ provide("KeyPairGenerator", "EC");
+ provide("Signature", "NONEWITHECDSA");
provide("Signature", "SHA1WITHECDSA");
provide("Signature", "SHA224WITHECDSA");
provide("Signature", "SHA256WITHECDSA");
diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java
index bd64360..425cefa 100644
--- a/support/src/test/java/libcore/java/security/TestKeyStore.java
+++ b/support/src/test/java/libcore/java/security/TestKeyStore.java
@@ -31,8 +31,6 @@
import java.io.ByteArrayInputStream;
import java.io.PrintStream;
import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -98,6 +96,9 @@
}
private static final boolean TEST_MANAGERS = true;
+ private static final byte[] LOCAL_HOST_ADDRESS = { 127, 0, 0, 1 };
+ private static final String LOCAL_HOST_NAME = "localhost";
+
public final KeyStore keyStore;
public final char[] storePassword;
@@ -156,16 +157,12 @@
.signer(ROOT_CA.getPrivateKey("RSA", "RSA"))
.rootCa(ROOT_CA.getRootCertificate("RSA"))
.build();
- try {
- SERVER = new Builder()
- .aliasPrefix("server")
- .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
- .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
- .addSubjectAltNameIpAddress(InetAddress.getLocalHost().getAddress())
- .build();
- } catch (UnknownHostException e) {
- throw new RuntimeException(e);
- }
+ SERVER = new Builder()
+ .aliasPrefix("server")
+ .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
+ .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
+ .addSubjectAltNameIpAddress(LOCAL_HOST_ADDRESS)
+ .build();
CLIENT = new TestKeyStore(createClient(INTERMEDIATE_CA.keyStore), null, null);
CLIENT_CERTIFICATE = new Builder()
.aliasPrefix("client")
@@ -529,11 +526,7 @@
}
private X500Principal localhost() {
- try {
- return new X500Principal("CN=" + InetAddress.getLocalHost().getHostName());
- } catch (UnknownHostException e) {
- throw new RuntimeException(e);
- }
+ return new X500Principal("CN=" + LOCAL_HOST_NAME);
}
}
diff --git a/support/src/test/java/tests/resources/ClassPathURLStreamHandlerTest.jar b/support/src/test/java/tests/resources/ClassPathURLStreamHandlerTest.jar
new file mode 100644
index 0000000..ef810a7
--- /dev/null
+++ b/support/src/test/java/tests/resources/ClassPathURLStreamHandlerTest.jar
Binary files differ
diff --git a/support/src/test/java/tests/resources/StrictJarFileTestNoManifest.jar b/support/src/test/java/tests/resources/StrictJarFileTestNoManifest.jar
new file mode 100644
index 0000000..f16cb2a
--- /dev/null
+++ b/support/src/test/java/tests/resources/StrictJarFileTestNoManifest.jar
Binary files differ
diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java
index a90db3b..2e32bf1 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlParser.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java
@@ -604,6 +604,7 @@
}
read('>');
+ skip();
}
/**
diff --git a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
index bfdeece..399462d 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
@@ -27,17 +27,13 @@
public class KXmlSerializer implements XmlSerializer {
+ private static final int BUFFER_LEN = 8192;
+ private final char[] mText = new char[BUFFER_LEN];
+ private int mPos;
+
// static final String UNDEFINED = ":";
- // BEGIN android-added
- /** size (in characters) for the write buffer */
- private static final int WRITE_BUFFER_SIZE = 500;
- // END android-added
-
- // BEGIN android-changed
- // (Guarantee that the writer is always buffered.)
- private BufferedWriter writer;
- // END android-changed
+ private Writer writer;
private boolean pending;
private int auto;
@@ -52,6 +48,41 @@
private boolean unicode;
private String encoding;
+ private void append(char c) throws IOException {
+ if (mPos >= BUFFER_LEN) {
+ flushBuffer();
+ }
+ mText[mPos++] = c;
+ }
+
+ private void append(String str, int i, int length) throws IOException {
+ while (length > 0) {
+ if (mPos == BUFFER_LEN) {
+ flushBuffer();
+ }
+ int batch = BUFFER_LEN - mPos;
+ if (batch > length) {
+ batch = length;
+ }
+ str.getChars(i, i + batch, mText, mPos);
+ i += batch;
+ length -= batch;
+ mPos += batch;
+ }
+ }
+
+ private void append(String str) throws IOException {
+ append(str, 0, str.length());
+ }
+
+ private final void flushBuffer() throws IOException {
+ if(mPos > 0) {
+ writer.write(mText, 0, mPos);
+ writer.flush();
+ mPos = 0;
+ }
+ }
+
private final void check(boolean close) throws IOException {
if (!pending)
return;
@@ -67,17 +98,16 @@
indent[depth] = indent[depth - 1];
for (int i = nspCounts[depth - 1]; i < nspCounts[depth]; i++) {
- writer.write(' ');
- writer.write("xmlns");
+ append(" xmlns");
if (!nspStack[i * 2].isEmpty()) {
- writer.write(':');
- writer.write(nspStack[i * 2]);
+ append(':');
+ append(nspStack[i * 2]);
}
else if (getNamespace().isEmpty() && !nspStack[i * 2 + 1].isEmpty())
throw new IllegalStateException("Cannot set default namespace for elements in no namespace");
- writer.write("=\"");
+ append("=\"");
writeEscaped(nspStack[i * 2 + 1], '"');
- writer.write('"');
+ append('"');
}
if (nspCounts.length <= depth + 1) {
@@ -89,7 +119,11 @@
nspCounts[depth + 1] = nspCounts[depth];
// nspCounts[depth + 2] = nspCounts[depth];
- writer.write(close ? " />" : ">");
+ if (close) {
+ append(" />");
+ } else {
+ append('>');
+ }
}
private final void writeEscaped(String s, int quot) throws IOException {
@@ -100,22 +134,22 @@
case '\r':
case '\t':
if(quot == -1)
- writer.write(c);
+ append(c);
else
- writer.write("&#"+((int) c)+';');
+ append("&#"+((int) c)+';');
break;
case '&' :
- writer.write("&");
+ append("&");
break;
case '>' :
- writer.write(">");
+ append(">");
break;
case '<' :
- writer.write("<");
+ append("<");
break;
default:
if (c == quot) {
- writer.write(c == '"' ? """ : "'");
+ append(c == '"' ? """ : "'");
break;
}
// BEGIN android-changed: refuse to output invalid characters
@@ -128,9 +162,9 @@
boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
if (allowedInXml) {
if (unicode || c < 127) {
- writer.write(c);
+ append(c);
} else {
- writer.write("&#" + ((int) c) + ";");
+ append("&#" + ((int) c) + ";");
}
} else if (Character.isHighSurrogate(c) && i < s.length() - 1) {
writeSurrogate(c, s.charAt(i + 1));
@@ -157,9 +191,9 @@
}*/
public void docdecl(String dd) throws IOException {
- writer.write("<!DOCTYPE");
- writer.write(dd);
- writer.write(">");
+ append("<!DOCTYPE");
+ append(dd);
+ append('>');
}
public void endDocument() throws IOException {
@@ -171,9 +205,9 @@
public void entityRef(String name) throws IOException {
check(false);
- writer.write('&');
- writer.write(name);
- writer.write(';');
+ append('&');
+ append(name);
+ append(';');
}
public boolean getFeature(String name) {
@@ -301,14 +335,7 @@
}
public void setOutput(Writer writer) {
- // BEGIN android-changed
- // Guarantee that the writer is always buffered.
- if (writer instanceof BufferedWriter) {
- this.writer = (BufferedWriter) writer;
- } else {
- this.writer = new BufferedWriter(writer, WRITE_BUFFER_SIZE);
- }
- // END android-changed
+ this.writer = writer;
// elementStack = new String[12]; //nsp/prefix/name
//nspCounts = new int[4];
@@ -343,7 +370,7 @@
}
public void startDocument(String encoding, Boolean standalone) throws IOException {
- writer.write("<?xml version='1.0' ");
+ append("<?xml version='1.0' ");
if (encoding != null) {
this.encoding = encoding;
@@ -353,18 +380,17 @@
}
if (this.encoding != null) {
- writer.write("encoding='");
- writer.write(this.encoding);
- writer.write("' ");
+ append("encoding='");
+ append(this.encoding);
+ append("' ");
}
if (standalone != null) {
- writer.write("standalone='");
- writer.write(
- standalone.booleanValue() ? "yes" : "no");
- writer.write("' ");
+ append("standalone='");
+ append(standalone.booleanValue() ? "yes" : "no");
+ append("' ");
}
- writer.write("?>");
+ append("?>");
}
public XmlSerializer startTag(String namespace, String name)
@@ -375,9 +401,9 @@
// namespace = "";
if (indent[depth]) {
- writer.write("\r\n");
+ append("\r\n");
for (int i = 0; i < depth; i++)
- writer.write(" ");
+ append(" ");
}
int esp = depth * 3;
@@ -407,13 +433,13 @@
elementStack[esp++] = prefix;
elementStack[esp] = name;
- writer.write('<');
+ append('<');
if (!prefix.isEmpty()) {
- writer.write(prefix);
- writer.write(':');
+ append(prefix);
+ append(':');
}
- writer.write(name);
+ append(name);
pending = true;
@@ -457,24 +483,24 @@
}
*/
- writer.write(' ');
+ append(' ');
if (!prefix.isEmpty()) {
- writer.write(prefix);
- writer.write(':');
+ append(prefix);
+ append(':');
}
- writer.write(name);
- writer.write('=');
+ append(name);
+ append('=');
char q = value.indexOf('"') == -1 ? '"' : '\'';
- writer.write(q);
+ append(q);
writeEscaped(value, q);
- writer.write(q);
+ append(q);
return this;
}
public void flush() throws IOException {
check(false);
- writer.flush();
+ flushBuffer();
}
/*
public void close() throws IOException {
@@ -503,19 +529,19 @@
}
else {
if (indent[depth + 1]) {
- writer.write("\r\n");
+ append("\r\n");
for (int i = 0; i < depth; i++)
- writer.write(" ");
+ append(" ");
}
- writer.write("</");
+ append("</");
String prefix = elementStack[depth * 3 + 1];
if (!prefix.isEmpty()) {
- writer.write(prefix);
- writer.write(':');
+ append(prefix);
+ append(':');
}
- writer.write(name);
- writer.write('>');
+ append(name);
+ append('>');
}
nspCounts[depth + 1] = nspCounts[depth];
@@ -552,24 +578,24 @@
// BEGIN android-changed: ]]> is not allowed within a CDATA,
// so break and start a new one when necessary.
data = data.replace("]]>", "]]]]><![CDATA[>");
- writer.write("<![CDATA[");
+ append("<![CDATA[");
for (int i = 0; i < data.length(); ++i) {
char ch = data.charAt(i);
boolean allowedInCdata = (ch >= 0x20 && ch <= 0xd7ff) ||
(ch == '\t' || ch == '\n' || ch == '\r') ||
(ch >= 0xe000 && ch <= 0xfffd);
if (allowedInCdata) {
- writer.write(ch);
+ append(ch);
} else if (Character.isHighSurrogate(ch) && i < data.length() - 1) {
// Character entities aren't valid in CDATA, so break out for this.
- writer.write("]]>");
+ append("]]>");
writeSurrogate(ch, data.charAt(++i));
- writer.write("<![CDATA[");
+ append("<![CDATA[");
} else {
reportInvalidCharacter(ch);
}
}
- writer.write("]]>");
+ append("]]>");
// END android-changed
}
@@ -583,22 +609,22 @@
// seems likely to upset anything expecting modified UTF-8 rather than "real" UTF-8. It seems more
// conservative in a Java environment to use an entity reference instead.
int codePoint = Character.toCodePoint(high, low);
- writer.write("&#" + codePoint + ";");
+ append("&#" + codePoint + ";");
}
// END android-added
public void comment(String comment) throws IOException {
check(false);
- writer.write("<!--");
- writer.write(comment);
- writer.write("-->");
+ append("<!--");
+ append(comment);
+ append("-->");
}
public void processingInstruction(String pi)
throws IOException {
check(false);
- writer.write("<?");
- writer.write(pi);
- writer.write("?>");
+ append("<?");
+ append(pi);
+ append("?>");
}
}
diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
index 0958bbf..bb3d130 100644
--- a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
+++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
@@ -916,7 +916,7 @@
* Get next parsing event - element content will be coalesced and only one
* TEXT event must be returned for whole element content
* (comments and processing instructions will be ignored and entity references
- * must be expanded or exception mus be thrown if entity reference can not be expanded).
+ * must be expanded or exception must be thrown if entity reference can not be expanded).
* If element content is empty (content is "") then no TEXT event will be reported.
*
* <p><b>NOTE:</b> empty element (such as <tag/>) will be reported