am 42db8377: am 15e6f173: am d353b8e2: am ddc91700: am 0a0379d4: am 297a7de2: am 183efbb4: am 4629f94a: am e0350a80: am afd7d947: Merge "JarFile: make test chain 3 long" into jb-dev
* commit '42db8377fdd20cdff6cac1994b25382551270a26':
JarFile: make test chain 3 long
diff --git a/NativeCode.mk b/NativeCode.mk
index 4d19147..733f62e 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -82,7 +82,7 @@
LOCAL_CPPFLAGS += $(core_cppflags)
LOCAL_SRC_FILES += $(core_src_files)
LOCAL_C_INCLUDES += $(core_c_includes)
-LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libcrypto libexpat libicuuc libicui18n libnativehelper libz libutils
+LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libcrypto libdl libexpat libicuuc libicui18n libnativehelper libz libutils
LOCAL_STATIC_LIBRARIES += $(core_static_libraries) libziparchive
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libjavacore
diff --git a/benchmarks/src/benchmarks/regression/StringCaseMappingBenchmark.java b/benchmarks/src/benchmarks/regression/StringCaseMappingBenchmark.java
index ba5b59e..cde257b 100644
--- a/benchmarks/src/benchmarks/regression/StringCaseMappingBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/StringCaseMappingBenchmark.java
@@ -78,6 +78,14 @@
}
}
+ // toUpperCase for Greek is an extra-hard case that uses icu4c's Transliterator.
+ public void timeToUpperCase_el_GR(int reps) {
+ Locale el_GR = new Locale("el", "GR");
+ for (int i = 0; i < reps; ++i) {
+ s.value.toUpperCase(el_GR);
+ }
+ }
+
public void timeToLowerCase_US(int reps) {
for (int i = 0; i < reps; ++i) {
s.value.toUpperCase(Locale.US);
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index d2fd5da..f1a8013 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -16,15 +16,15 @@
package dalvik.system;
+import android.system.ErrnoException;
+import android.system.StructStat;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
-import libcore.io.ErrnoException;
import libcore.io.Libcore;
-import libcore.io.StructStat;
/**
* Manipulates DEX files. The class is similar in principle to
@@ -34,7 +34,7 @@
* read-only by the VM.
*/
public final class DexFile {
- private int mCookie;
+ private long mCookie;
private final String mFileName;
private final CloseGuard guard = CloseGuard.get();
@@ -215,7 +215,7 @@
return defineClass(name, loader, mCookie, suppressed);
}
- private static Class defineClass(String name, ClassLoader loader, int cookie,
+ private static Class defineClass(String name, ClassLoader loader, long cookie,
List<Throwable> suppressed) {
Class result = null;
try {
@@ -232,9 +232,6 @@
return result;
}
- private static native Class defineClassNative(String name, ClassLoader loader, int cookie)
- throws ClassNotFoundException, NoClassDefFoundError;
-
/**
* Enumerate the names of the classes in this DEX file.
*
@@ -266,9 +263,6 @@
}
}
- /* return a String array with class names */
- native private static String[] getClassNameList(int cookie);
-
/**
* Called when the class is finalized. Makes sure the DEX file is closed.
*
@@ -291,20 +285,17 @@
* Open a DEX file. The value returned is a magic VM cookie. On
* failure, an IOException is thrown.
*/
- private static int openDexFile(String sourceName, String outputName,
- int flags) throws IOException {
+ private static long openDexFile(String sourceName, String outputName, int flags) throws IOException {
return openDexFileNative(new File(sourceName).getCanonicalPath(),
(outputName == null) ? null : new File(outputName).getCanonicalPath(),
flags);
}
- native private static int openDexFileNative(String sourceName, String outputName,
- int flags) throws IOException;
-
- /*
- * Close DEX file.
- */
- native private static void closeDexFile(int cookie);
+ private static native void closeDexFile(long cookie);
+ private static native Class defineClassNative(String name, ClassLoader loader, long cookie)
+ throws ClassNotFoundException, NoClassDefFoundError;
+ private static native String[] getClassNameList(long cookie);
+ private static native long openDexFileNative(String sourceName, String outputName, int flags);
/**
* Returns true if the VM believes that the apk/jar file is out of date
@@ -320,6 +311,13 @@
* @throws dalvik.system.StaleDexCacheError if the optimized dex file
* is stale but exists on a read-only partition.
*/
- native public static boolean isDexOptNeeded(String fileName)
+ public static native boolean isDexOptNeeded(String fileName)
+ throws FileNotFoundException, IOException;
+
+ /**
+ * @hide
+ **/
+ public static native boolean isDexOptNeededInternal(String fileName, String pkgname,
+ boolean defer)
throws FileNotFoundException, IOException;
}
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index f3bee10..e364e40 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -16,6 +16,8 @@
package dalvik.system;
+import android.system.ErrnoException;
+import android.system.StructStat;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
@@ -26,11 +28,9 @@
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipFile;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import libcore.io.StructStat;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* A pair of lists of entries, associated with a {@code ClassLoader}.
diff --git a/dalvik/src/main/java/dalvik/system/Zygote.java b/dalvik/src/main/java/dalvik/system/Zygote.java
deleted file mode 100644
index ea9dbdc..0000000
--- a/dalvik/src/main/java/dalvik/system/Zygote.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2006 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 dalvik.system;
-
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-
-import java.io.File;
-
-/**
- * Provides access to the Dalvik "zygote" feature, which allows a VM instance to
- * be partially initialized and then fork()'d from the partially initialized
- * state.
- *
- * @hide
- */
-public class Zygote {
- /*
- * Bit values for "debugFlags" argument. The definitions are duplicated
- * in the native code.
- */
- /** enable debugging over JDWP */
- public static final int DEBUG_ENABLE_DEBUGGER = 1;
- /** enable JNI checks */
- public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1;
- /** enable Java programming language "assert" statements */
- public static final int DEBUG_ENABLE_ASSERT = 1 << 2;
- /** disable the JIT compiler */
- public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3;
- /** Enable logging of third-party JNI activity. */
- public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
-
- /** No external storage should be mounted. */
- public static final int MOUNT_EXTERNAL_NONE = 0;
- /** Single-user external storage should be mounted. */
- public static final int MOUNT_EXTERNAL_SINGLEUSER = 1;
- /** Multi-user external storage should be mounted. */
- public static final int MOUNT_EXTERNAL_MULTIUSER = 2;
- /** All multi-user external storage should be mounted. */
- public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3;
-
- /**
- * When set by the system server, all subsequent apps will be launched in
- * VM safe mode.
- */
- public static boolean systemInSafeMode = false;
-
- private Zygote() {}
-
- private static void preFork() {
- Daemons.stop();
- waitUntilAllThreadsStopped();
- }
-
- /**
- * We must not fork until we're single-threaded again. Wait until /proc shows we're
- * down to just one thread.
- */
- private static void waitUntilAllThreadsStopped() {
- File tasks = new File("/proc/self/task");
- while (tasks.list().length > 1) {
- try {
- // Experimentally, booting and playing about with a stingray, I never saw us
- // go round this loop more than once with a 10ms sleep.
- Thread.sleep(10);
- } catch (InterruptedException ignored) {
- }
- }
- }
-
- private static void postFork() {
- Daemons.start();
- }
-
- /**
- * Forks a new Zygote instance, but does not leave the zygote mode.
- * The current VM must have been started with the -Xzygote flag. The
- * new child is expected to eventually call forkAndSpecialize()
- *
- * @return 0 if this is the child, pid of the child
- * if this is the parent, or -1 on error
- */
- public static int fork() {
- preFork();
- int pid = nativeFork();
- postFork();
- return pid;
- }
-
- native public static int nativeFork();
-
- /**
- * Forks a new VM instance. The current VM must have been started
- * with the -Xzygote flag. <b>NOTE: new instance keeps all
- * root capabilities. The new process is expected to call capset()</b>.
- *
- * @param uid the UNIX uid that the new process should setuid() to after
- * fork()ing and and before spawning any threads.
- * @param gid the UNIX gid that the new process should setgid() to after
- * fork()ing and and before spawning any threads.
- * @param gids null-ok; a list of UNIX gids that the new process should
- * setgroups() to after fork and before spawning any threads.
- * @param debugFlags bit flags that enable debugging features.
- * @param rlimits null-ok an array of rlimit tuples, with the second
- * dimension having a length of 3 and representing
- * (resource, rlim_cur, rlim_max). These are set via the posix
- * setrlimit(2) call.
- * @param seInfo null-ok a string specifying SELinux information for
- * the new process.
- * @param niceName null-ok a string specifying the process name.
- *
- * @return 0 if this is the child, pid of the child
- * if this is the parent, or -1 on error.
- */
- public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
- int[][] rlimits, int mountExternal, String seInfo, String niceName) {
- preFork();
- int pid = nativeForkAndSpecialize(
- uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName);
- postFork();
- return pid;
- }
-
- native public static int nativeForkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
- int[][] rlimits, int mountExternal, String seInfo, String niceName);
-
- /**
- * Special method to start the system server process. In addition to the
- * common actions performed in forkAndSpecialize, the pid of the child
- * process is recorded such that the death of the child process will cause
- * zygote to exit.
- *
- * @param uid the UNIX uid that the new process should setuid() to after
- * fork()ing and and before spawning any threads.
- * @param gid the UNIX gid that the new process should setgid() to after
- * fork()ing and and before spawning any threads.
- * @param gids null-ok; a list of UNIX gids that the new process should
- * setgroups() to after fork and before spawning any threads.
- * @param debugFlags bit flags that enable debugging features.
- * @param rlimits null-ok an array of rlimit tuples, with the second
- * dimension having a length of 3 and representing
- * (resource, rlim_cur, rlim_max). These are set via the posix
- * setrlimit(2) call.
- * @param permittedCapabilities argument for setcap()
- * @param effectiveCapabilities argument for setcap()
- *
- * @return 0 if this is the child, pid of the child
- * if this is the parent, or -1 on error.
- */
- public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
- int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
- preFork();
- int pid = nativeForkSystemServer(
- uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
- postFork();
- return pid;
- }
-
- native public static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
- int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
-
- /**
- * Executes "/system/bin/sh -c <command>" using the exec() system call.
- * This method throws a runtime exception if exec() failed, otherwise, this
- * method never returns.
- *
- * @param command The shell command to execute.
- */
- public static void execShell(String command) {
- String[] args = { "/system/bin/sh", "-c", command };
- try {
- Libcore.os.execv(args[0], args);
- } catch (ErrnoException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Appends quotes shell arguments to the specified string builder.
- * The arguments are quoted using single-quotes, escaped if necessary,
- * prefixed with a space, and appended to the command.
- *
- * @param command A string builder for the shell command being constructed.
- * @param args An array of argument strings to be quoted and appended to the command.
- * @see #execShell(String)
- */
- public static void appendQuotedShellArgs(StringBuilder command, String[] args) {
- for (String arg : args) {
- command.append(" '").append(arg.replace("'", "'\\''")).append("'");
- }
- }
-}
diff --git a/dalvik/src/main/java/dalvik/system/ZygoteHooks.java b/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
new file mode 100644
index 0000000..fa4f392
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2006 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 dalvik.system;
+
+import java.io.File;
+
+/**
+ * Provides hooks for the zygote to call back into the runtime to perform
+ * parent or child specific initialization..
+ *
+ * @hide
+ */
+public final class ZygoteHooks {
+ private long token;
+
+ /**
+ * Called by the zygote prior to every fork. Each call to {@code preFork}
+ * is followed by a matching call to {@link #postForkChild(int)} on the child
+ * process and {@link #postForkCommon()} on both the parent and the child
+ * process. {@code postForkCommon} is called after {@code postForkChild} in
+ * the child process.
+ */
+ public void preFork() {
+ Daemons.stop();
+ waitUntilAllThreadsStopped();
+ token = nativePreFork();
+ }
+
+ /**
+ * Called by the zygote in the child process after every fork. The debug
+ * flags from {@code debugFlags} are applied to the child process.
+ */
+ public void postForkChild(int debugFlags) {
+ nativePostForkChild(token, debugFlags);
+ }
+
+ /**
+ * Called by the zygote in both the parent and child processes after
+ * every fork. In the child process, this method is called after
+ * {@code postForkChild}.
+ */
+ public void postForkCommon() {
+ Daemons.start();
+ }
+
+ private static native long nativePreFork();
+ private static native void nativePostForkChild(long token, int debugFlags);
+
+ /**
+ * We must not fork until we're single-threaded again. Wait until /proc shows we're
+ * down to just one thread.
+ */
+ private static void waitUntilAllThreadsStopped() {
+ File tasks = new File("/proc/self/task");
+ while (tasks.list().length > 1) {
+ try {
+ // Experimentally, booting and playing about with a stingray, I never saw us
+ // go round this loop more than once with a 10ms sleep.
+ Thread.sleep(10);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+}
diff --git a/dex/src/main/java/com/android/dex/Dex.java b/dex/src/main/java/com/android/dex/Dex.java
index 116a33c..6667c99 100644
--- a/dex/src/main/java/com/android/dex/Dex.java
+++ b/dex/src/main/java/com/android/dex/Dex.java
@@ -469,7 +469,7 @@
}
/**
- * Look up a type index index from a class def index.
+ * Look up an annotation directory offset from a class def index.
*/
public int annotationDirectoryOffsetFromClassDefIndex(int classDefIndex) {
checkBounds(classDefIndex, tableOfContents.classDefs.size);
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
index 93c9a5c..3412e1c 100644
--- a/expectations/brokentests.txt
+++ b/expectations/brokentests.txt
@@ -875,5 +875,13 @@
"org.apache.harmony.tests.java.util.jar.ManifestTest#testRead",
"org.apache.harmony.tests.java.util.jar.ManifestTest#testStreamConstructor"
]
+},
+{
+ description: "Potentially flakey because they rely on a specific local TCP port being free.",
+ result: EXEC_FAILED,
+ names: [
+ "org.apache.harmony.tests.java.nio.channels.ServerSocketChannelTest#test_bind_explicitPort",
+ "org.apache.harmony.tests.java.nio.channels.SocketChannelTest#testBind_ExplicitFreePort"
+ ]
}
]
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 66b39fb..b976c67 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -939,49 +939,6 @@
name: "libcore.java.sql.OldStatementTest#testGetUpdateCount"
},
{
- description: "Handshake Status is never finished. NPE in ClientSessionContext$HostAndPort.hashCode() when host
- is null",
- bug: 3403706,
- name: "tests.api.javax.net.ssl.SSLEngineTest#testHandshake"
-},
-{
- description: "com.android.org.conscrypt.SSLEngineImpl#getDelegatedTask() throws NPE instead of
- returning null",
- bug: 3403706,
- name: "tests.api.javax.net.ssl.SSLEngineTest#test_getDelegatedTask"
-},
-{
- description: "Fixed in DonutBurger, boundary checks missing",
- bug: 3403706,
- name: "tests.api.javax.net.ssl.SSLEngineTest#test_unwrap_02"
-},
-{
- description: "Fixed on DonutBurger, Wrong Exception thrown",
- bug: 3403706,
- names: [
- "tests.api.javax.net.ssl.SSLEngineTest#test_unwrap_03",
- "tests.api.javax.net.ssl.SSLEngineTest#test_unwrap_04",
- "tests.api.javax.net.ssl.SSLEngineTest#test_unwrap_ByteBuffer$ByteBuffer_02",
- "tests.api.javax.net.ssl.SSLEngineTest#test_unwrap_ByteBuffer$ByteBuffer_03",
- "tests.api.javax.net.ssl.SSLEngineTest#test_unwrap_ByteBuffer_ByteBuffer_02",
- "tests.api.javax.net.ssl.SSLEngineTest#test_unwrap_ByteBuffer_ByteBuffer_03"
- ]
-},
-{
- description: "Fixed in DonutBurger, boundary checks missing",
- bug: 3403706,
- name: "tests.api.javax.net.ssl.SSLEngineTest#test_wrap_02"
-},
-{
- description: "Fixed on DonutBurger, Wrong Exception thrown",
- bug: 3403706,
- names: [
- "tests.api.javax.net.ssl.SSLEngineTest#test_wrap_04",
- "tests.api.javax.net.ssl.SSLEngineTest#test_wrap_ByteBuffer$ByteBuffer_03",
- "tests.api.javax.net.ssl.SSLEngineTest#test_wrap_ByteBuffer_ByteBuffer_03"
- ]
-},
-{
description: "ManagerFactoryParameters object is not supported and InvalidAlgorithmParameterException was
thrown.",
bug: 3403706,
@@ -993,26 +950,6 @@
name: "tests.api.javax.net.ssl.HostnameVerifierTest#testVerifyIpAddress"
},
{
- description: "NO SERVER CERTIFICATE FOUND - selectSuite should not pick a suite that needs a certificate if it is missing",
- bug: 3045163,
- name: "libcore.javax.net.ssl.SSLEngineTest#test_SSLEngine_beginHandshake_noKeyStore"
-},
-{
- description: "AlertException instead of SSLException",
- bug: 3045163,
- name: "libcore.javax.net.ssl.SSLEngineTest#test_SSLEngine_setEnableSessionCreation_client"
-},
-{
- description: "SSLException instead of failure to handshake",
- bug: 3045163,
- name: "libcore.javax.net.ssl.SSLEngineTest#test_SSLEngine_setEnableSessionCreation_server"
-},
-{
- description: "SSLHandshakeException instead of failure to handshake",
- bug: 3045163,
- name: "libcore.javax.net.ssl.SSLEngineTest#test_SSLEngine_setUseClientMode"
-},
-{
description: "method test fails once in a while. Cannot be sure that exception is thrown in every test execution.",
bug: 3403706,
name: "libcore.sqlite.OldDatabaseTest#testBusy_handler"
@@ -1698,8 +1635,6 @@
description: "Known failures in SerializationStressTest",
bug: 11668227,
names: [
- "org.apache.harmony.tests.java.io.SerializationStressTest3#test_18_113_writeObject",
- "org.apache.harmony.tests.java.io.SerializationStressTest3#test_18_115_writeObject",
"org.apache.harmony.tests.java.io.SerializationStressTest4#test_writeObject_Collections_UnmodifiableMap_UnmodifiableEntrySet"
]
},
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/file/FileURLConnectionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/file/FileURLConnectionTest.java
index 41f4b39..317ba5c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/file/FileURLConnectionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/internal/net/www/protocol/file/FileURLConnectionTest.java
@@ -76,7 +76,10 @@
conn = new FileURLConnection(url);
assertNotNull(conn.getInputStream());
assertEquals(conn.getContentType(), conn.getHeaderField("content-type"));
- assertEquals(Integer.toString(conn.getContentLength()), conn.getHeaderField("content-length"));
+ assertEquals(Integer.toString(conn.getContentLength()),
+ conn.getHeaderField("content-length"));
+ assertEquals(Long.toString(conn.getContentLengthLong()),
+ conn.getHeaderField("content-length"));
assertEquals(conn.getHeaderField(0), conn.getHeaderField("content-type"));
assertEquals(conn.getHeaderField(1), conn.getHeaderField("content-length"));
assertEquals(conn.getHeaderField(2), conn.getHeaderField("last-modified"));
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileOutputStreamTest.java
index 8c5e91f..6263cc5 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileOutputStreamTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileOutputStreamTest.java
@@ -91,8 +91,9 @@
String fileName = f.getAbsolutePath();
fos = new FileOutputStream(fileName);
- // Regression test for HARMONY-4012
- new FileOutputStream("nul");
+ // Harmony 4012.
+ fos = new FileOutputStream("/dev/null");
+ fos.close();
}
/**
@@ -196,14 +197,13 @@
.length()).equals(fileString));
// Regression test for HARMONY-285
- File file = new File("FileOutputStream.tmp");
- file.deleteOnExit();
+ File file = File.createTempFile("FileOutputStreamTest", ".tmp");
FileOutputStream out = new FileOutputStream(file);
try {
out.write(null, 0, 0);
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
- // Expected
+ fail();
+ } catch (NullPointerException expected) {
+
} finally {
out.close();
file.delete();
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
index affe913..ea7b2ea 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
@@ -2009,7 +2009,11 @@
* java.io.File#toString()
*/
public void test_toString() {
- String fileName = System.getProperty("user.home") + File.separator + "input.tst";
+ String fileName = System.getProperty("user.home");
+ if (!fileName.endsWith(File.separator)) {
+ fileName += File.separator;
+ }
+ fileName += "input.tst";
File f = new File(fileName);
assertEquals("Incorrect string returned", fileName, f.toString());
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest.java
index c6371ea..5d9b18c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest.java
@@ -184,9 +184,9 @@
symbols.setZoneStrings(new String[][] { { "a", "b", "c", "d", "e" },
{ "f", "g", "h", "i", "j" } });
((java.text.SimpleDateFormat) DATEFORM).setDateFormatSymbols(symbols);
- DATEFORM.setNumberFormat(new java.text.DecimalFormat("#.#;'-'#.#"));
+ DATEFORM.setNumberFormat(new java.text.DecimalFormat("#0.#"));
DATEFORM.setTimeZone(TimeZone.getTimeZone("EST"));
- ((java.text.DecimalFormat) NUMBERFORM).applyPattern("#.#;'-'#.#");
+ ((java.text.DecimalFormat) NUMBERFORM).applyPattern("#0.#");
MESSAGE.setFormat(0, DATEFORM);
MESSAGE.setFormat(1, DATEFORM);
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java
index 1e569e8..c5dd4f0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/SerializationStressTest4.java
@@ -721,35 +721,6 @@
}
- public void test_writeObject_Collections_UnmodifiableMap_UnmodifiableEntrySet() throws Exception {
- // Test for method void
- // java.io.ObjectOutputStream.writeObject(java.util.Collections.UnmodifiableMap.UnmodifiableEntrySet)
-
- Object objToSave = null;
- Object objLoaded = null;
-
-
- objToSave = java.util.Collections.unmodifiableMap(MAP).entrySet();
- if (DEBUG)
- System.out.println("Obj = " + objToSave);
- objLoaded = dumpAndReload(objToSave);
-
- // Has to have worked
- boolean equals;
- equals = ((java.util.Collection) objToSave).size() == ((java.util.Collection) objLoaded)
- .size();
- if (equals) {
- java.util.Iterator iter1 = ((java.util.Collection) objToSave)
- .iterator(), iter2 = ((java.util.Collection) objLoaded)
- .iterator();
- while (iter1.hasNext())
- equals = equals && iter1.next().equals(iter2.next());
- }
- assertTrue(MSG_TEST_FAILED + objToSave, equals);
-
-
- }
-
public void test_writeObject_NumberFormat() {
// Test for method void
// java.io.ObjectOutputStream.writeObject(java.text.NumberFormat)
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java
index a8bbe26..eaaae86 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/CharacterTest.java
@@ -80,7 +80,6 @@
}
public void test_charCountI() {
-
for (int c = '\u0000'; c <= '\uFFFF'; c++) {
assertEquals(1, Character.charCount(c));
}
@@ -109,14 +108,11 @@
@SuppressWarnings("cast")
public void test_codePointAtLjava_lang_CharSequenceI() {
-
- assertEquals('a', Character.codePointAt((CharSequence) "abc", 0));
- assertEquals('b', Character.codePointAt((CharSequence) "abc", 1));
- assertEquals('c', Character.codePointAt((CharSequence) "abc", 2));
- assertEquals(0x10000, Character.codePointAt(
- (CharSequence) "\uD800\uDC00", 0));
- assertEquals('\uDC00', Character.codePointAt(
- (CharSequence) "\uD800\uDC00", 1));
+ assertEquals('a', Character.codePointAt("abc", 0));
+ assertEquals('b', Character.codePointAt("abc", 1));
+ assertEquals('c', Character.codePointAt("abc", 2));
+ assertEquals(0x10000, Character.codePointAt("\uD800\uDC00", 0));
+ assertEquals('\uDC00', Character.codePointAt("\uD800\uDC00", 1));
try {
Character.codePointAt((CharSequence) null, 0);
@@ -125,27 +121,24 @@
}
try {
- Character.codePointAt((CharSequence) "abc", -1);
+ Character.codePointAt("abc", -1);
fail("No IOOBE, negative index.");
} catch (IndexOutOfBoundsException e) {
}
try {
- Character.codePointAt((CharSequence) "abc", 4);
+ Character.codePointAt("abc", 4);
fail("No IOOBE, index too large.");
} catch (IndexOutOfBoundsException e) {
}
}
public void test_codePointAt$CI() {
-
assertEquals('a', Character.codePointAt("abc".toCharArray(), 0));
assertEquals('b', Character.codePointAt("abc".toCharArray(), 1));
assertEquals('c', Character.codePointAt("abc".toCharArray(), 2));
- assertEquals(0x10000, Character.codePointAt("\uD800\uDC00"
- .toCharArray(), 0));
- assertEquals('\uDC00', Character.codePointAt("\uD800\uDC00"
- .toCharArray(), 1));
+ assertEquals(0x10000, Character.codePointAt("\uD800\uDC00".toCharArray(), 0));
+ assertEquals('\uDC00', Character.codePointAt("\uD800\uDC00".toCharArray(), 1));
try {
Character.codePointAt((char[]) null, 0);
@@ -167,19 +160,15 @@
}
public void test_codePointAt$CII() {
-
assertEquals('a', Character.codePointAt("abc".toCharArray(), 0, 3));
assertEquals('b', Character.codePointAt("abc".toCharArray(), 1, 3));
assertEquals('c', Character.codePointAt("abc".toCharArray(), 2, 3));
- assertEquals(0x10000, Character.codePointAt("\uD800\uDC00"
- .toCharArray(), 0, 2));
- assertEquals('\uDC00', Character.codePointAt("\uD800\uDC00"
- .toCharArray(), 1, 2));
- assertEquals('\uD800', Character.codePointAt("\uD800\uDC00"
- .toCharArray(), 0, 1));
+ assertEquals(0x10000, Character.codePointAt("\uD800\uDC00".toCharArray(), 0, 2));
+ assertEquals('\uDC00', Character.codePointAt("\uD800\uDC00".toCharArray(), 1, 2));
+ assertEquals('\uD800', Character.codePointAt("\uD800\uDC00".toCharArray(), 0, 1));
try {
- Character.codePointAt((char[]) null, 0, 1);
+ Character.codePointAt(null, 0, 1);
fail("No NPE.");
} catch (NullPointerException e) {
}
@@ -211,14 +200,11 @@
@SuppressWarnings("cast")
public void test_codePointBeforeLjava_lang_CharSequenceI() {
-
- assertEquals('a', Character.codePointBefore((CharSequence) "abc", 1));
- assertEquals('b', Character.codePointBefore((CharSequence) "abc", 2));
- assertEquals('c', Character.codePointBefore((CharSequence) "abc", 3));
- assertEquals(0x10000, Character.codePointBefore(
- (CharSequence) "\uD800\uDC00", 2));
- assertEquals('\uD800', Character.codePointBefore(
- (CharSequence) "\uD800\uDC00", 1));
+ assertEquals('a', Character.codePointBefore("abc", 1));
+ assertEquals('b', Character.codePointBefore("abc", 2));
+ assertEquals('c', Character.codePointBefore("abc", 3));
+ assertEquals(0x10000, Character.codePointBefore("\uD800\uDC00", 2));
+ assertEquals('\uD800', Character.codePointBefore("\uD800\uDC00", 1));
try {
Character.codePointBefore((CharSequence) null, 0);
@@ -227,27 +213,24 @@
}
try {
- Character.codePointBefore((CharSequence) "abc", 0);
+ Character.codePointBefore("abc", 0);
fail("No IOOBE, index below one.");
} catch (IndexOutOfBoundsException e) {
}
try {
- Character.codePointBefore((CharSequence) "abc", 4);
+ Character.codePointBefore("abc", 4);
fail("No IOOBE, index too large.");
} catch (IndexOutOfBoundsException e) {
}
}
public void test_codePointBefore$CI() {
-
assertEquals('a', Character.codePointBefore("abc".toCharArray(), 1));
assertEquals('b', Character.codePointBefore("abc".toCharArray(), 2));
assertEquals('c', Character.codePointBefore("abc".toCharArray(), 3));
- assertEquals(0x10000, Character.codePointBefore("\uD800\uDC00"
- .toCharArray(), 2));
- assertEquals('\uD800', Character.codePointBefore("\uD800\uDC00"
- .toCharArray(), 1));
+ assertEquals(0x10000, Character.codePointBefore("\uD800\uDC00".toCharArray(), 2));
+ assertEquals('\uD800', Character.codePointBefore("\uD800\uDC00".toCharArray(), 1));
try {
Character.codePointBefore((char[]) null, 0);
@@ -269,19 +252,15 @@
}
public void test_codePointBefore$CII() {
-
assertEquals('a', Character.codePointBefore("abc".toCharArray(), 1, 0));
assertEquals('b', Character.codePointBefore("abc".toCharArray(), 2, 0));
assertEquals('c', Character.codePointBefore("abc".toCharArray(), 3, 0));
- assertEquals(0x10000, Character.codePointBefore("\uD800\uDC00"
- .toCharArray(), 2, 0));
- assertEquals('\uDC00', Character.codePointBefore("\uD800\uDC00"
- .toCharArray(), 2, 1));
- assertEquals('\uD800', Character.codePointBefore("\uD800\uDC00"
- .toCharArray(), 1, 0));
+ assertEquals(0x10000, Character.codePointBefore("\uD800\uDC00".toCharArray(), 2, 0));
+ assertEquals('\uDC00', Character.codePointBefore("\uD800\uDC00".toCharArray(), 2, 1));
+ assertEquals('\uD800', Character.codePointBefore("\uD800\uDC00".toCharArray(), 1, 0));
try {
- Character.codePointBefore((char[]) null, 1, 0);
+ Character.codePointBefore(null, 1, 0);
fail("No NPE.");
} catch (NullPointerException e) {
}
@@ -355,14 +334,10 @@
}
public void test_toCharsI() {
- assertTrue(Arrays.equals(new char[] { '\uD800', '\uDC00' }, Character
- .toChars(0x10000)));
- assertTrue(Arrays.equals(new char[] { '\uD800', '\uDC01' }, Character
- .toChars(0x10001)));
- assertTrue(Arrays.equals(new char[] { '\uD801', '\uDC01' }, Character
- .toChars(0x10401)));
- assertTrue(Arrays.equals(new char[] { '\uDBFF', '\uDFFF' }, Character
- .toChars(0x10FFFF)));
+ assertTrue(Arrays.equals(new char[] { '\uD800', '\uDC00' }, Character.toChars(0x10000)));
+ assertTrue(Arrays.equals(new char[] { '\uD800', '\uDC01' }, Character.toChars(0x10001)));
+ assertTrue(Arrays.equals(new char[] { '\uD801', '\uDC01' }, Character.toChars(0x10401)));
+ assertTrue(Arrays.equals(new char[] { '\uDBFF', '\uDFFF' }, Character.toChars(0x10FFFF)));
try {
Character.toChars(Integer.MAX_VALUE);
@@ -431,7 +406,7 @@
assertEquals(2, result);
try {
- Character.offsetByCodePoints((CharSequence) null, 0, 1);
+ Character.offsetByCodePoints(null, 0, 1);
fail();
} catch (NullPointerException e) {
}
@@ -462,48 +437,37 @@
}
public void test_offsetByCodePoints$CIIII() {
- int result = Character.offsetByCodePoints("a\uD800\uDC00b"
- .toCharArray(), 0, 4, 0, 2);
+ int result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), 0, 4, 0, 2);
assertEquals(3, result);
- result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(),
- 0, 4, 0, 3);
+ result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), 0, 4, 0, 3);
assertEquals(4, result);
- result = Character.offsetByCodePoints("a\uD800\uDC00b\uD800c"
- .toCharArray(), 0, 5, 0, 3);
+ result = Character.offsetByCodePoints("a\uD800\uDC00b\uD800c".toCharArray(), 0, 5, 0, 3);
assertEquals(4, result);
- result = Character
- .offsetByCodePoints("abcd".toCharArray(), 0, 4, 3, -1);
+ result = Character.offsetByCodePoints("abcd".toCharArray(), 0, 4, 3, -1);
assertEquals(2, result);
- result = Character
- .offsetByCodePoints("abcd".toCharArray(), 1, 2, 3, -2);
+ result = Character.offsetByCodePoints("abcd".toCharArray(), 1, 2, 3, -2);
assertEquals(1, result);
- result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(),
- 0, 4, 3, -1);
+ result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), 0, 4, 3, -1);
assertEquals(1, result);
- result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(),
- 0, 2, 2, -1);
+ result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), 0, 2, 2, -1);
assertEquals(1, result);
- result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(),
- 0, 4, 3, 0);
+ result = Character.offsetByCodePoints("a\uD800\uDC00b".toCharArray(), 0, 4, 3, 0);
assertEquals(3, result);
- result = Character.offsetByCodePoints("\uD800\uDC00bc".toCharArray(),
- 0, 4, 3, 0);
+ result = Character.offsetByCodePoints("\uD800\uDC00bc".toCharArray(), 0, 4, 3, 0);
assertEquals(3, result);
- result = Character.offsetByCodePoints("a\uDC00bc".toCharArray(), 0, 4,
- 3, -1);
+ result = Character.offsetByCodePoints("a\uDC00bc".toCharArray(), 0, 4, 3, -1);
assertEquals(2, result);
- result = Character.offsetByCodePoints("a\uD800bc".toCharArray(), 0, 4,
- 3, -1);
+ result = Character.offsetByCodePoints("a\uD800bc".toCharArray(), 0, 4, 3, -1);
assertEquals(2, result);
try {
@@ -590,7 +554,6 @@
}
public void test_codePointAt_Invalid() {
-
try {
Character.codePointAt(null, 6, 4);
fail("Expected IndexOutOfBoundsException");
@@ -624,8 +587,7 @@
* java.lang.Character#charValue()
*/
public void test_charValue() {
- assertEquals("Incorrect char value returned", 'T', new Character('T')
- .charValue());
+ assertEquals("Incorrect char value returned", 'T', new Character('T').charValue());
}
/**
@@ -638,12 +600,9 @@
Character z = new Character('d');
assertEquals("Returned false for same Character", 0, c.compareTo(c));
- assertEquals("Returned false for identical Character",
- 0, c.compareTo(x));
- assertTrue("Returned other than less than for lesser char", c
- .compareTo(y) > 0);
- assertTrue("Returned other than greater than for greater char", c
- .compareTo(z) < 0);
+ assertEquals("Returned false for identical Character", 0, c.compareTo(x));
+ assertTrue("Returned other than less than for lesser char", c.compareTo(y) > 0);
+ assertTrue("Returned other than greater than for greater char", c.compareTo(z) < 0);
}
/**
@@ -675,10 +634,8 @@
*/
public void test_equalsLjava_lang_Object() {
// Test for method boolean java.lang.Character.equals(java.lang.Object)
- assertTrue("Equality test failed", new Character('A')
- .equals(new Character('A')));
- assertTrue("Equality test failed", !(new Character('A')
- .equals(new Character('a'))));
+ assertTrue("Equality test failed", new Character('A').equals(new Character('A')));
+ assertFalse("Equality test failed", (new Character('A').equals(new Character('a'))));
}
/**
@@ -692,8 +649,7 @@
Character.forDigit(i, hexChars.length) == hexChars[i]);
}
- char decimalChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
- '9' };
+ char decimalChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
for (int i = 0; i < decimalChars.length; i++) {
assertTrue(
"Returned incorrect char for " + Integer.toString(i),
@@ -706,18 +662,13 @@
* java.lang.Character#getNumericValue(char)
*/
public void test_getNumericValueC() {
- assertEquals("Returned incorrect numeric value 1", 1, Character
- .getNumericValue('1'));
- assertEquals("Returned incorrect numeric value 2", 15, Character
- .getNumericValue('F'));
- assertEquals("Returned incorrect numeric value 3", -1, Character
- .getNumericValue('\u221e'));
- assertEquals("Returned incorrect numeric value 4", -2, Character
- .getNumericValue('\u00be'));
- assertEquals("Returned incorrect numeric value 5", 10000, Character
- .getNumericValue('\u2182'));
- assertEquals("Returned incorrect numeric value 6", 2, Character
- .getNumericValue('\uff12'));
+ assertEquals("Returned incorrect numeric value 1", 1, Character.getNumericValue('1'));
+ assertEquals("Returned incorrect numeric value 2", 15, Character.getNumericValue('F'));
+ assertEquals("Returned incorrect numeric value 3", -1, Character.getNumericValue('\u221e'));
+ assertEquals("Returned incorrect numeric value 4", -2, Character.getNumericValue('\u00be'));
+ assertEquals("Returned incorrect numeric value 5", 10000,
+ Character.getNumericValue('\u2182'));
+ assertEquals("Returned incorrect numeric value 6", 2, Character.getNumericValue('\uff12'));
}
/**
@@ -746,9 +697,7 @@
assertEquals(10, Character.getNumericValue(0x0061));
assertEquals(35, Character.getNumericValue(0x007A));
assertEquals(10, Character.getNumericValue(0xFF21));
-
- //FIXME depends on ICU4J
- //assertEquals(35, Character.getNumericValue(0xFF3A));
+ assertEquals(35, Character.getNumericValue(0xFF3A));
assertEquals(10, Character.getNumericValue(0xFF41));
assertEquals(35, Character.getNumericValue(0xFF5A));
@@ -776,12 +725,11 @@
Character.getType('_') == Character.CONNECTOR_PUNCTUATION);
assertTrue("Returned incorrect type for: $",
Character.getType('$') == Character.CURRENCY_SYMBOL);
- assertTrue("Returned incorrect type for: \u2029", Character
- .getType('\u2029') == Character.PARAGRAPH_SEPARATOR);
+ assertTrue("Returned incorrect type for: \u2029",
+ Character.getType('\u2029') == Character.PARAGRAPH_SEPARATOR);
assertEquals("Wrong constant for FORMAT", 16, Character.FORMAT);
- assertEquals("Wrong constant for PRIVATE_USE",
- 18, Character.PRIVATE_USE);
+ assertEquals("Wrong constant for PRIVATE_USE", 18, Character.PRIVATE_USE);
}
/**
@@ -893,8 +841,7 @@
* java.lang.Character#hashCode()
*/
public void test_hashCode() {
- assertEquals("Incorrect hash returned",
- 89, new Character('Y').hashCode());
+ assertEquals("Incorrect hash returned", 89, new Character('Y').hashCode());
}
/**
@@ -902,8 +849,7 @@
*/
public void test_isDefinedC() {
assertTrue("Defined character returned false", Character.isDefined('v'));
- assertTrue("Defined character returned false", Character
- .isDefined('\u6039'));
+ assertTrue("Defined character returned false", Character.isDefined('\u6039'));
}
/**
@@ -924,7 +870,7 @@
*/
public void test_isDigitC() {
assertTrue("Digit returned false", Character.isDigit('1'));
- assertTrue("Non-Digit returned false", !Character.isDigit('A'));
+ assertFalse("Non-Digit returned false", Character.isDigit('A'));
}
/**
@@ -966,24 +912,23 @@
* java.lang.Character#isIdentifierIgnorable(char)
*/
public void test_isIdentifierIgnorableC() {
- assertTrue("Ignorable whitespace returned false", Character
- .isIdentifierIgnorable('\u0007'));
+ assertTrue("Ignorable whitespace returned false",
+ Character.isIdentifierIgnorable('\u0007'));
assertTrue("Ignorable non - whitespace control returned false",
Character.isIdentifierIgnorable('\u000f'));
- assertTrue("Ignorable join control returned false", Character
- .isIdentifierIgnorable('\u200e'));
+ assertTrue("Ignorable join control returned false",
+ Character.isIdentifierIgnorable('\u200e'));
// the spec is wrong, and our implementation is correct
- assertTrue("Ignorable bidi control returned false", Character
- .isIdentifierIgnorable('\u202b'));
+ assertTrue("Ignorable bidi control returned false",
+ Character.isIdentifierIgnorable('\u202b'));
- assertTrue("Ignorable format control returned false", Character
- .isIdentifierIgnorable('\u206c'));
- assertTrue("Ignorable zero-width no-break returned false", Character
- .isIdentifierIgnorable('\ufeff'));
+ assertTrue("Ignorable format control returned false",
+ Character.isIdentifierIgnorable('\u206c'));
+ assertTrue("Ignorable zero-width no-break returned false",
+ Character.isIdentifierIgnorable('\ufeff'));
- assertTrue("Non-Ignorable returned true", !Character
- .isIdentifierIgnorable('\u0065'));
+ assertFalse("Non-Ignorable returned true", Character.isIdentifierIgnorable('\u0065'));
}
/**
@@ -1035,13 +980,13 @@
*/
public void test_isISOControlC() {
// Test for method boolean java.lang.Character.isISOControl(char)
- for (int i = 0; i < 32; i++)
- assertTrue("ISOConstrol char returned false", Character
- .isISOControl((char) i));
+ for (int i = 0; i < 32; i++) {
+ assertTrue("ISOConstrol char returned false", Character.isISOControl((char) i));
+ }
- for (int i = 127; i < 160; i++)
- assertTrue("ISOConstrol char returned false", Character
- .isISOControl((char) i));
+ for (int i = 127; i < 160; i++) {
+ assertTrue("ISOConstrol char returned false", Character.isISOControl((char) i));
+ }
}
/**
@@ -1049,18 +994,17 @@
*/
public void test_isISOControlI() {
// Test for method boolean java.lang.Character.isISOControl(char)
- for (int i = 0; i < 32; i++)
- assertTrue("ISOConstrol char returned false", Character
- .isISOControl(i));
+ for (int i = 0; i < 32; i++) {
+ assertTrue("ISOConstrol char returned false", Character.isISOControl(i));
+ }
- for (int i = 127; i < 160; i++)
- assertTrue("ISOConstrol char returned false", Character
- .isISOControl(i));
+ for (int i = 127; i < 160; i++) {
+ assertTrue("ISOConstrol char returned false", Character.isISOControl(i));
+ }
- for (int i = 160; i < 260; i++)
- assertFalse("Not ISOConstrol char returned true", Character
- .isISOControl(i));
-
+ for (int i = 160; i < 260; i++) {
+ assertFalse("Not ISOConstrol char returned true", Character.isISOControl(i));
+ }
}
@@ -1069,14 +1013,11 @@
*/
public void test_isJavaIdentifierPartC() {
assertTrue("letter returned false", Character.isJavaIdentifierPart('l'));
- assertTrue("currency returned false", Character
- .isJavaIdentifierPart('$'));
+ assertTrue("currency returned false", Character.isJavaIdentifierPart('$'));
assertTrue("digit returned false", Character.isJavaIdentifierPart('9'));
- assertTrue("connecting char returned false", Character
- .isJavaIdentifierPart('_'));
- assertTrue("ignorable control returned true", !Character
- .isJavaIdentifierPart('\u200b'));
- assertTrue("semi returned true", !Character.isJavaIdentifierPart(';'));
+ assertTrue("connecting char returned false", Character.isJavaIdentifierPart('_'));
+ assertTrue("ignorable control returned false", Character.isJavaIdentifierPart('\u200b'));
+ assertFalse("semi returned true", Character.isJavaIdentifierPart(';'));
}
/**
@@ -1123,7 +1064,6 @@
assertTrue(Character.isJavaIdentifierPart(0x00AD));
assertTrue(Character.isJavaIdentifierPart(0xE007F));
- //RI fails because 0x200B changes category in Unicode 4.1
assertTrue(Character.isJavaIdentifierPart(0x200B));
}
@@ -1131,16 +1071,12 @@
* java.lang.Character#isJavaIdentifierStart(char)
*/
public void test_isJavaIdentifierStartC() {
- assertTrue("letter returned false", Character
- .isJavaIdentifierStart('l'));
- assertTrue("currency returned false", Character
- .isJavaIdentifierStart('$'));
- assertTrue("connecting char returned false", Character
- .isJavaIdentifierStart('_'));
- assertTrue("digit returned true", !Character.isJavaIdentifierStart('9'));
- assertTrue("ignorable control returned true", !Character
- .isJavaIdentifierStart('\u200b'));
- assertTrue("semi returned true", !Character.isJavaIdentifierStart(';'));
+ assertTrue("letter returned false", Character.isJavaIdentifierStart('l'));
+ assertTrue("currency returned false", Character.isJavaIdentifierStart('$'));
+ assertTrue("connecting char returned false", Character.isJavaIdentifierStart('_'));
+ assertFalse("digit returned true", Character.isJavaIdentifierStart('9'));
+ assertFalse("ignorable control returned true", Character.isJavaIdentifierStart('\u200b'));
+ assertFalse("semi returned true", Character.isJavaIdentifierStart(';'));
}
/**
@@ -1184,13 +1120,11 @@
public void test_isJavaLetterC() {
assertTrue("letter returned false", Character.isJavaLetter('l'));
assertTrue("currency returned false", Character.isJavaLetter('$'));
- assertTrue("connecting char returned false", Character
- .isJavaLetter('_'));
+ assertTrue("connecting char returned false", Character.isJavaLetter('_'));
- assertTrue("digit returned true", !Character.isJavaLetter('9'));
- assertTrue("ignored control returned true", !Character
- .isJavaLetter('\u200b'));
- assertTrue("semi returned true", !Character.isJavaLetter(';'));
+ assertFalse("digit returned true", Character.isJavaLetter('9'));
+ assertFalse("ignored control returned true", Character.isJavaLetter('\u200b'));
+ assertFalse("semi returned true", Character.isJavaLetter(';'));
}
/**
@@ -1199,12 +1133,10 @@
@SuppressWarnings("deprecation")
public void test_isJavaLetterOrDigitC() {
assertTrue("letter returned false", Character.isJavaLetterOrDigit('l'));
- assertTrue("currency returned false", Character
- .isJavaLetterOrDigit('$'));
+ assertTrue("currency returned false", Character.isJavaLetterOrDigit('$'));
assertTrue("digit returned false", Character.isJavaLetterOrDigit('9'));
- assertTrue("connecting char returned false", Character
- .isJavaLetterOrDigit('_'));
- assertTrue("semi returned true", !Character.isJavaLetterOrDigit(';'));
+ assertTrue("connecting char returned false", Character.isJavaLetterOrDigit('_'));
+ assertFalse("semi returned true", Character.isJavaLetterOrDigit(';'));
}
/**
@@ -1212,7 +1144,7 @@
*/
public void test_isLetterC() {
assertTrue("Letter returned false", Character.isLetter('L'));
- assertTrue("Non-Letter returned true", !Character.isLetter('9'));
+ assertFalse("Non-Letter returned true", Character.isLetter('9'));
}
/**
@@ -1237,8 +1169,8 @@
public void test_isLetterOrDigitC() {
assertTrue("Digit returned false", Character.isLetterOrDigit('9'));
assertTrue("Letter returned false", Character.isLetterOrDigit('K'));
- assertTrue("Control returned true", !Character.isLetterOrDigit('\n'));
- assertTrue("Punctuation returned true", !Character.isLetterOrDigit('?'));
+ assertFalse("Control returned true", Character.isLetterOrDigit('\n'));
+ assertFalse("Punctuation returned true", Character.isLetterOrDigit('?'));
}
/**
@@ -1268,7 +1200,7 @@
*/
public void test_isLowerCaseC() {
assertTrue("lower returned false", Character.isLowerCase('a'));
- assertTrue("upper returned true", !Character.isLowerCase('T'));
+ assertFalse("upper returned true", Character.isLowerCase('T'));
}
/**
@@ -1293,7 +1225,7 @@
public void test_isSpaceC() {
// Test for method boolean java.lang.Character.isSpace(char)
assertTrue("space returned false", Character.isSpace('\n'));
- assertTrue("non-space returned true", !Character.isSpace('T'));
+ assertFalse("non-space returned true", Character.isSpace('T'));
}
/**
@@ -1301,7 +1233,7 @@
*/
public void test_isSpaceCharC() {
assertTrue("space returned false", Character.isSpaceChar('\u0020'));
- assertTrue("non-space returned true", !Character.isSpaceChar('\n'));
+ assertFalse("non-space returned true", Character.isSpaceChar('\n'));
}
/**
@@ -1337,9 +1269,11 @@
if (Character.isTitleCase(c)) {
tnum++;
int i;
- for (i = 0; i < tChars.length; i++)
- if (tChars[i] == c)
+ for (i = 0; i < tChars.length; i++) {
+ if (tChars[i] == c) {
i = tChars.length + 1;
+ }
+ }
if (i < tChars.length) {
fail("Non Title Case char returned true");
}
@@ -1359,8 +1293,8 @@
0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc,
0x1fcc, 0x1ffc };
- for (int i = 0; i < titleCaseCharacters.length; i++) {
- assertTrue(Character.isTitleCase(titleCaseCharacters[i]));
+ for (int titleCaseCharacter : titleCaseCharacters) {
+ assertTrue(Character.isTitleCase(titleCaseCharacter));
}
assertFalse(Character.isTitleCase(0x110000));
@@ -1372,7 +1306,7 @@
public void test_isUnicodeIdentifierPartC() {
assertTrue("'a' returned false", Character.isUnicodeIdentifierPart('a'));
assertTrue("'2' returned false", Character.isUnicodeIdentifierPart('2'));
- assertTrue("'+' returned true", !Character.isUnicodeIdentifierPart('+'));
+ assertFalse("'+' returned true", Character.isUnicodeIdentifierPart('+'));
}
/**
@@ -1426,19 +1360,15 @@
* java.lang.Character#isUnicodeIdentifierStart(char)
*/
public void test_isUnicodeIdentifierStartC() {
- assertTrue("'a' returned false", Character
- .isUnicodeIdentifierStart('a'));
- assertTrue("'2' returned true", !Character
- .isUnicodeIdentifierStart('2'));
- assertTrue("'+' returned true", !Character
- .isUnicodeIdentifierStart('+'));
+ assertTrue("'a' returned false", Character.isUnicodeIdentifierStart('a'));
+ assertFalse("'2' returned true", Character.isUnicodeIdentifierStart('2'));
+ assertFalse("'+' returned true", Character.isUnicodeIdentifierStart('+'));
}
/**
* java.lang.Character#isUnicodeIdentifierStart(int)
*/
public void test_isUnicodeIdentifierStart_I() {
-
assertTrue(Character.isUnicodeIdentifierStart((int) 'a'));
assertFalse(Character.isUnicodeIdentifierStart((int) '2'));
assertFalse(Character.isUnicodeIdentifierStart((int) '+'));
@@ -1466,7 +1396,7 @@
* java.lang.Character#isUpperCase(char)
*/
public void test_isUpperCaseC() {
- assertTrue("Incorrect case value", !Character.isUpperCase('t'));
+ assertFalse("Incorrect case value", Character.isUpperCase('t'));
assertTrue("Incorrect case value", Character.isUpperCase('T'));
}
@@ -1490,7 +1420,7 @@
*/
public void test_isWhitespaceC() {
assertTrue("space returned false", Character.isWhitespace('\n'));
- assertTrue("non-space returned true", !Character.isWhitespace('T'));
+ assertFalse("non-space returned true", Character.isWhitespace('T'));
}
/**
@@ -1522,8 +1452,7 @@
assertFalse(Character.isWhitespace(0xFEFF));
- //FIXME depend on ICU4J
- //assertFalse(Character.isWhitespace(0x2007));
+ assertFalse(Character.isWhitespace(0x2007));
}
@@ -1578,12 +1507,9 @@
* java.lang.Character#toTitleCase(char)
*/
public void test_toTitleCaseC() {
- assertEquals("Incorrect title case for a",
- 'A', Character.toTitleCase('a'));
- assertEquals("Incorrect title case for A",
- 'A', Character.toTitleCase('A'));
- assertEquals("Incorrect title case for 1",
- '1', Character.toTitleCase('1'));
+ assertEquals("Incorrect title case for a", 'A', Character.toTitleCase('a'));
+ assertEquals("Incorrect title case for A", 'A', Character.toTitleCase('A'));
+ assertEquals("Incorrect title case for 1", '1', Character.toTitleCase('1'));
}
/**
@@ -1606,12 +1532,9 @@
*/
public void test_toUpperCaseC() {
// Test for method char java.lang.Character.toUpperCase(char)
- assertEquals("Incorrect upper case for a",
- 'A', Character.toUpperCase('a'));
- assertEquals("Incorrect upper case for A",
- 'A', Character.toUpperCase('A'));
- assertEquals("Incorrect upper case for 1",
- '1', Character.toUpperCase('1'));
+ assertEquals("Incorrect upper case for a", 'A', Character.toUpperCase('a'));
+ assertEquals("Incorrect upper case for A", 'A', Character.toUpperCase('A'));
+ assertEquals("Incorrect upper case for 1", '1', Character.toUpperCase('1'));
}
/**
@@ -1633,101 +1556,82 @@
* java.lang.Character#getDirectionality(int)
*/
public void test_isDirectionaliy_I() {
- assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character
- .getDirectionality(0xFFFE));
- assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character
- .getDirectionality(0x30000));
- assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character
- .getDirectionality(0x110000));
- assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character
- .getDirectionality(-1));
+ assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character.getDirectionality(0xFFFE));
+ assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character.getDirectionality(0x30000));
+ assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character.getDirectionality(0x110000));
+ assertEquals(Character.DIRECTIONALITY_UNDEFINED, Character.getDirectionality(-1));
- assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT, Character
- .getDirectionality(0x0041));
- assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT, Character
- .getDirectionality(0x10000));
- assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT, Character
- .getDirectionality(0x104A9));
+ assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT, Character.getDirectionality(0x0041));
+ assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT, Character.getDirectionality(0x10000));
+ assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT, Character.getDirectionality(0x104A9));
- assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT, Character
- .getDirectionality(0xFB4F));
- assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT, Character
- .getDirectionality(0x10838));
+ assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT, Character.getDirectionality(0xFB4F));
+ assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT, Character.getDirectionality(0x10838));
// Unicode standard 5.1 changed category of unicode point 0x0600 from AL to AN
- assertEquals(Character.DIRECTIONALITY_ARABIC_NUMBER, Character
- .getDirectionality(0x0600));
- assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, Character
- .getDirectionality(0xFEFC));
+ assertEquals(Character.DIRECTIONALITY_ARABIC_NUMBER, Character.getDirectionality(0x0600));
+ assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC,
+ Character.getDirectionality(0xFEFC));
- assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER, Character
- .getDirectionality(0x2070));
- assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER, Character
- .getDirectionality(0x1D7FF));
+ assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER, Character.getDirectionality(0x2070));
+ assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER,
+ Character.getDirectionality(0x1D7FF));
- //RI fails ,this is non-bug difference between Unicode 4.0 and 4.1
- assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, Character
- .getDirectionality(0x002B));
- assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, Character
- .getDirectionality(0xFF0B));
+ assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR,
+ Character.getDirectionality(0x002B));
+ assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR,
+ Character.getDirectionality(0xFF0B));
- assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, Character
- .getDirectionality(0x0023));
- assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, Character
- .getDirectionality(0x17DB));
+ assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR,
+ Character.getDirectionality(0x0023));
+ assertEquals(Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR,
+ Character.getDirectionality(0x17DB));
- assertEquals(Character.DIRECTIONALITY_ARABIC_NUMBER, Character
- .getDirectionality(0x0660));
- assertEquals(Character.DIRECTIONALITY_ARABIC_NUMBER, Character
- .getDirectionality(0x066C));
+ assertEquals(Character.DIRECTIONALITY_ARABIC_NUMBER, Character.getDirectionality(0x0660));
+ assertEquals(Character.DIRECTIONALITY_ARABIC_NUMBER, Character.getDirectionality(0x066C));
- assertEquals(Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, Character
- .getDirectionality(0x002C));
- assertEquals(Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, Character
- .getDirectionality(0xFF1A));
+ assertEquals(Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR,
+ Character.getDirectionality(0x002C));
+ assertEquals(Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR,
+ Character.getDirectionality(0xFF1A));
- assertEquals(Character.DIRECTIONALITY_NONSPACING_MARK, Character
- .getDirectionality(0x17CE));
- assertEquals(Character.DIRECTIONALITY_NONSPACING_MARK, Character
- .getDirectionality(0xE01DB));
+ assertEquals(Character.DIRECTIONALITY_NONSPACING_MARK, Character.getDirectionality(0x17CE));
+ assertEquals(Character.DIRECTIONALITY_NONSPACING_MARK,
+ Character.getDirectionality(0xE01DB));
- assertEquals(Character.DIRECTIONALITY_BOUNDARY_NEUTRAL, Character
- .getDirectionality(0x0000));
- assertEquals(Character.DIRECTIONALITY_BOUNDARY_NEUTRAL, Character
- .getDirectionality(0xE007F));
+ assertEquals(Character.DIRECTIONALITY_BOUNDARY_NEUTRAL,
+ Character.getDirectionality(0x0000));
+ assertEquals(Character.DIRECTIONALITY_BOUNDARY_NEUTRAL,
+ Character.getDirectionality(0xE007F));
- assertEquals(Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR, Character
- .getDirectionality(0x000A));
- assertEquals(Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR, Character
- .getDirectionality(0x2029));
+ assertEquals(Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR,
+ Character.getDirectionality(0x000A));
+ assertEquals(Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR,
+ Character.getDirectionality(0x2029));
- assertEquals(Character.DIRECTIONALITY_SEGMENT_SEPARATOR, Character
- .getDirectionality(0x0009));
- assertEquals(Character.DIRECTIONALITY_SEGMENT_SEPARATOR, Character
- .getDirectionality(0x001F));
+ assertEquals(Character.DIRECTIONALITY_SEGMENT_SEPARATOR,
+ Character.getDirectionality(0x0009));
+ assertEquals(Character.DIRECTIONALITY_SEGMENT_SEPARATOR,
+ Character.getDirectionality(0x001F));
- assertEquals(Character.DIRECTIONALITY_WHITESPACE, Character
- .getDirectionality(0x0020));
- assertEquals(Character.DIRECTIONALITY_WHITESPACE, Character
- .getDirectionality(0x3000));
+ assertEquals(Character.DIRECTIONALITY_WHITESPACE, Character.getDirectionality(0x0020));
+ assertEquals(Character.DIRECTIONALITY_WHITESPACE, Character.getDirectionality(0x3000));
- assertEquals(Character.DIRECTIONALITY_OTHER_NEUTRALS, Character
- .getDirectionality(0x2FF0));
- assertEquals(Character.DIRECTIONALITY_OTHER_NEUTRALS, Character
- .getDirectionality(0x1D356));
+ assertEquals(Character.DIRECTIONALITY_OTHER_NEUTRALS, Character.getDirectionality(0x2FF0));
+ assertEquals(Character.DIRECTIONALITY_OTHER_NEUTRALS, Character.getDirectionality(0x1D356));
- assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, Character
- .getDirectionality(0x202A));
+ assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING,
+ Character.getDirectionality(0x202A));
- assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, Character
- .getDirectionality(0x202D));
+ assertEquals(Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE,
+ Character.getDirectionality(0x202D));
- assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, Character
- .getDirectionality(0x202B));
+ assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING,
+ Character.getDirectionality(0x202B));
- assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, Character
- .getDirectionality(0x202E));
+ assertEquals(Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE,
+ Character.getDirectionality(0x202E));
- assertEquals(Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT, Character
- .getDirectionality(0x202C));
+ assertEquals(Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT,
+ Character.getDirectionality(0x202C));
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/HttpCookieTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/HttpCookieTest.java
index b513f2a..306e697 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/HttpCookieTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/HttpCookieTest.java
@@ -234,6 +234,16 @@
assertFalse(cookie.getSecure());
}
+ public void test_Is_SetHttpOnly() {
+ HttpCookie cookie = new HttpCookie("testName", "value");
+ assertFalse(cookie.isHttpOnly());
+
+ cookie.setHttpOnly(true);
+ assertTrue(cookie.isHttpOnly());
+ cookie.setHttpOnly(false);
+ assertFalse(cookie.isHttpOnly());
+ }
+
/**
* java.net.HttpCookie#getPath(), setPath(String)
* @since 1.6
@@ -803,6 +813,32 @@
}
}
+ public void test_Parse_httpOnly() {
+ // Default is !httpOnly.
+ List<HttpCookie> list = HttpCookie.parse("Set-Cookie: SID=31d4d96e407aad42");
+ HttpCookie cookie = list.get(0);
+ assertFalse(cookie.isHttpOnly());
+
+ // Well formed, simple.
+ list = HttpCookie.parse("Set-Cookie: SID=31d4d96e407aad42; HttpOnly");
+ cookie = list.get(0);
+ assertTrue(cookie.isHttpOnly());
+
+ // Well formed, other attributes present.
+ list = HttpCookie.parse("Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly");
+ cookie = list.get(0);
+ assertTrue(cookie.isHttpOnly());
+ assertTrue(cookie.getSecure());
+ assertEquals("/", cookie.getPath());
+
+ // Mangled spacing, casing and attributes that have an (ignored) value.
+ list = HttpCookie.parse("Set-Cookie:SID=31d4d96e407aad42;Path=/;secure=false;httponly=false");
+ cookie = list.get(0);
+ assertTrue(cookie.isHttpOnly());
+ assertTrue(cookie.getSecure());
+ assertEquals("/", cookie.getPath());
+ }
+
/**
* java.net.HttpCookie#parse(String) for version conflict cases
* @since 1.6
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
index 96be9fd..6a75746 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
@@ -34,14 +34,6 @@
public class MulticastSocketTest extends junit.framework.TestCase {
- private boolean atLeastTwoInterfaces = false;
-
- private NetworkInterface networkInterface1 = null;
-
- private NetworkInterface networkInterface2 = null;
-
- private NetworkInterface IPV6networkInterface1 = null;
-
private static InetAddress lookup(String s) {
try {
return InetAddress.getByName(s);
@@ -127,6 +119,10 @@
}
}
+ private NetworkInterface loopbackInterface;
+ private NetworkInterface networkInterface1;
+ private NetworkInterface IPV6networkInterface1;
+
public void test_Constructor() throws IOException {
// regression test for 497
MulticastSocket s = new MulticastSocket();
@@ -188,11 +184,9 @@
assertEquals("getNetworkInterface did not return interface set by setNeworkInterface",
networkInterface1, mss.getNetworkInterface());
- if (atLeastTwoInterfaces) {
- mss.setNetworkInterface(networkInterface2);
- assertEquals("getNetworkInterface did not return network interface set by second setNetworkInterface call",
- networkInterface2, mss.getNetworkInterface());
- }
+ mss.setNetworkInterface(loopbackInterface);
+ assertEquals("getNetworkInterface did not return network interface set by second setNetworkInterface call",
+ loopbackInterface, mss.getNetworkInterface());
mss.close();
mss = new MulticastSocket(0);
@@ -331,18 +325,9 @@
}
public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface() throws Exception {
- // if there is more than one network interface then check that
- // we can join on specific interfaces and that we only receive
- // if data is received on that interface
- if (!atLeastTwoInterfaces) {
- return;
- }
- // set up server on first interfaces
- NetworkInterface loopbackInterface = NetworkInterface.getByInetAddress(
- InetAddress.getByName("127.0.0.1"));
-
- boolean anyLoop = networkInterface1.equals(loopbackInterface) ||
- networkInterface2.equals(loopbackInterface);
+ // Check that we can join on specific interfaces and that we only receive if data is
+ // received on that interface. This test is only really useful on devices with multiple
+ // non-loopback interfaces.
ArrayList<NetworkInterface> realInterfaces = new ArrayList<NetworkInterface>();
Enumeration<NetworkInterface> theInterfaces = NetworkInterface.getNetworkInterfaces();
@@ -355,6 +340,11 @@
for (int i = 0; i < realInterfaces.size(); i++) {
NetworkInterface thisInterface = realInterfaces.get(i);
+ if (!thisInterface.supportsMulticast()) {
+ // Skip interfaces that do not support multicast - there's no point in proving
+ // they cannot send / receive multicast messages.
+ continue;
+ }
// get the first address on the interface
@@ -367,34 +357,20 @@
if (addresses.hasMoreElements()) {
InetAddress firstAddress = addresses.nextElement();
if (firstAddress instanceof Inet4Address) {
- group = InetAddress.getByName("224.0.0.4");
- if (anyLoop) {
- if (networkInterface1.equals(loopbackInterface)) {
- sendingInterface = networkInterface2;
- } else {
- sendingInterface = networkInterface1;
- }
- } else {
- if (i == 1) {
- sendingInterface = networkInterface2;
- } else {
- sendingInterface = networkInterface1;
- }
- }
+ group = GOOD_IPv4;
+ sendingInterface = networkInterface1;
} else {
// if this interface only seems to support IPV6 addresses
- group = InetAddress.getByName("FF01:0:0:0:0:0:2:8001");
+ group = GOOD_IPv6;
sendingInterface = IPV6networkInterface1;
}
}
-
MulticastServer server = new MulticastServer(group, 0, thisInterface);
server.start();
Thread.sleep(1000);
- // Now send out a package on interface
- // networkInterface 1. We should
+ // Now send out a package on interface networkInterface 1. We should
// only see the packet if we send it on interface 1
MulticastSocket mss = new MulticastSocket(0);
mss.setNetworkInterface(sendingInterface);
@@ -430,7 +406,7 @@
MulticastSocket mss = new MulticastSocket(0);
SocketAddress groupSockAddr = new InetSocketAddress(group, mss.getLocalPort());
mss.joinGroup(groupSockAddr, networkInterface1);
- mss.joinGroup(groupSockAddr, networkInterface2);
+ mss.joinGroup(groupSockAddr, loopbackInterface);
try {
mss.joinGroup(groupSockAddr, networkInterface1);
fail("Did not get expected exception when joining for second time on same interface");
@@ -501,7 +477,6 @@
}
private void test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(InetAddress group, InetAddress group2) throws Exception {
- String msg = null;
SocketAddress groupSockAddr = null;
SocketAddress groupSockAddr2 = null;
@@ -524,14 +499,13 @@
}
mss.leaveGroup(groupSockAddr, networkInterface1);
- if (atLeastTwoInterfaces) {
- mss.joinGroup(groupSockAddr, networkInterface1);
- try {
- mss.leaveGroup(groupSockAddr, networkInterface2);
- fail("Did not get exception when trying to leave group on wrong interface " +
- "joined on [" + networkInterface1 + "] left on [" + networkInterface2 + "]");
- } catch (IOException expected) {
- }
+
+ mss.joinGroup(groupSockAddr, networkInterface1);
+ try {
+ mss.leaveGroup(groupSockAddr, loopbackInterface);
+ fail("Did not get exception when trying to leave group on wrong interface " +
+ "joined on [" + networkInterface1 + "] left on [" + loopbackInterface + "]");
+ } catch (IOException expected) {
}
}
@@ -806,51 +780,55 @@
@Override
protected void setUp() throws Exception {
+ // The loopback interface isn't actually useful for sending/receiving multicast messages
+ // but it can be used as a dummy for tests where that does not matter.
+ loopbackInterface = NetworkInterface.getByInetAddress(InetAddress.getLoopbackAddress());
+ assertNotNull(loopbackInterface);
+ assertTrue(loopbackInterface.isLoopback());
+ assertFalse(loopbackInterface.supportsMulticast());
+
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
// only consider interfaces that have addresses associated with them.
// Otherwise tests don't work so well
- if ( interfaces != null) {
+ if (interfaces != null) {
boolean atLeastOneInterface = false;
- while ( interfaces.hasMoreElements() && (atLeastOneInterface == false)) {
- networkInterface1 = interfaces.nextElement();
- if (isUpAndHasAddresses(networkInterface1)) {
+ while (interfaces.hasMoreElements() && (atLeastOneInterface == false)) {
+ networkInterface1 = interfaces.nextElement();
+ if (willWorkForMulticast(networkInterface1)) {
atLeastOneInterface = true;
}
}
- assertTrue(atLeastOneInterface);
+ assertTrue("Test environment must have at least one environment capable of multicast",
+ atLeastOneInterface);
- atLeastTwoInterfaces = false;
- if ( interfaces.hasMoreElements()) {
- while ( interfaces.hasMoreElements() && (atLeastTwoInterfaces == false)) {
- networkInterface2 = interfaces.nextElement();
- if (isUpAndHasAddresses(networkInterface2)) {
- atLeastTwoInterfaces = true;
- }
- }
- }
-
- // first the first interface that supports IPV6 if one exists
- interfaces = NetworkInterface.getNetworkInterfaces();
+ // Find the first multicast-compatible interface that supports IPV6 if one exists
+ interfaces = NetworkInterface.getNetworkInterfaces();
boolean found = false;
- while ( interfaces.hasMoreElements() && !found) {
+ while (interfaces.hasMoreElements() && !found) {
NetworkInterface nextInterface = interfaces.nextElement();
- Enumeration<InetAddress> addresses = nextInterface.getInetAddresses();
- while (addresses.hasMoreElements()) {
- final InetAddress nextAddress = addresses.nextElement();
- if (nextAddress instanceof Inet6Address) {
- IPV6networkInterface1 = nextInterface;
- found = true;
- break;
+ if (willWorkForMulticast(nextInterface)) {
+ Enumeration<InetAddress> addresses = nextInterface.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ final InetAddress nextAddress = addresses.nextElement();
+ if (nextAddress instanceof Inet6Address) {
+ IPV6networkInterface1 = nextInterface;
+ found = true;
+ break;
+ }
}
}
}
}
}
- private static boolean isUpAndHasAddresses(NetworkInterface iface) throws IOException {
- return iface.isUp() && iface.getInetAddresses().hasMoreElements();
+ private static boolean willWorkForMulticast(NetworkInterface iface) throws IOException {
+ return iface.isUp()
+ // Typically loopback interfaces do not support multicast, but we rule them out
+ // explicitly anyway.
+ && !iface.isLoopback() && iface.supportsMulticast()
+ && iface.getInetAddresses().hasMoreElements();
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java
index efec70f..d7ef745 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/ServerSocketTest.java
@@ -363,7 +363,8 @@
s = new ServerSocket(0);
try {
int portNumber = s.getLocalPort();
- assertEquals("ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport="
+ // In IPv6, the all-zeros-address is written as "::"
+ assertEquals("ServerSocket[addr=::/::,port=0,localport="
+ portNumber + "]", s.toString());
} finally {
s.close();
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java
index 53cbab1..18d2cb2 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java
@@ -70,7 +70,7 @@
public void run() {
try {
- ServerSocket socket = null;
+ ServerSocket socket;
switch (serverSocketConstructor) {
case FIRST_TIME:
socket = new ServerSocket(port, backlog,
@@ -537,7 +537,7 @@
assertTrue("Failed to create socket", client.getPort() == serverPort);
client.close();
- Socket theSocket = null;
+ Socket theSocket;
try {
theSocket = new Socket("127.0.0.1", serverPort, InetAddress
.getLocalHost(), 0);
@@ -627,9 +627,7 @@
*/
public void test_ConstructorLjava_net_Proxy_Exception() {
- SocketAddress addr1 = InetSocketAddress.createUnresolved("127.0.0.1",
- 80);
- SocketAddress addr2 = new InetSocketAddress("localhost", 80);
+ SocketAddress addr1 = InetSocketAddress.createUnresolved("127.0.0.1", 80);
Proxy proxy1 = new Proxy(Proxy.Type.HTTP, addr1);
// IllegalArgumentException test
@@ -1215,7 +1213,7 @@
OutputStream theOutput = worker.getOutputStream();
// Send the regular data
- byte[] sendBytes = new String("Test").getBytes();
+ byte[] sendBytes = "Test".getBytes();
theOutput.write(sendBytes);
theOutput.flush();
@@ -1268,7 +1266,7 @@
theOutput = worker.getOutputStream();
// Send the regular data
- sendBytes = new String("Test - Urgent Data").getBytes();
+ sendBytes = "Test - Urgent Data".getBytes();
theOutput.write(sendBytes);
// Send the urgent data (one byte) which should be received
@@ -1325,7 +1323,7 @@
theOutput = worker.getOutputStream();
// Send the regular data
- sendBytes = new String("Test - Urgent Data").getBytes();
+ sendBytes = "Test - Urgent Data".getBytes();
theOutput.write(sendBytes);
// Send the urgent data (one byte) which should be received
@@ -1421,7 +1419,7 @@
server.close();
// Regression test for HARMONY-1136
- new TestSocket((SocketImpl) null).setKeepAlive(true);
+ new TestSocket(null).setKeepAlive(true);
}
public void test_setOOBInlineZ() throws Exception {
@@ -1556,7 +1554,7 @@
client.shutdownInput();
// send the regular data
- String sendString = new String("Test");
+ String sendString = "Test";
theOutput.write(sendString.getBytes());
theOutput.flush();
@@ -1596,7 +1594,7 @@
worker.shutdownOutput();
// send the regular data
- String sendString = new String("Test");
+ String sendString = "Test";
try {
theOutput.write(sendString.getBytes());
theOutput.flush();
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java
index 0bdc3ff..731e907 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java
@@ -25,6 +25,7 @@
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
@@ -71,11 +72,11 @@
channel1 = DatagramChannel.open();
channel2 = DatagramChannel.open();
- channel1.socket().bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0));
- channel2.socket().bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0));
+ channel1.bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0));
+ channel2.bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0));
- channel1Address = (InetSocketAddress) channel1.socket().getLocalSocketAddress();
- channel2Address = (InetSocketAddress) channel2.socket().getLocalSocketAddress();
+ channel1Address = (InetSocketAddress) channel1.getLocalAddress();
+ channel2Address = (InetSocketAddress) channel2.getLocalAddress();
this.datagramSocket1 = new DatagramSocket(0, Inet6Address.LOOPBACK);
this.datagramSocket2 = new DatagramSocket(0, Inet6Address.LOOPBACK);
@@ -257,24 +258,39 @@
/**
* Test method for 'DatagramChannelImpl.socket()'
- *
- * @throws SocketException
*/
public void testSocket_BasicStatusBeforeConnect() throws Exception {
final DatagramChannel dc = DatagramChannel.open();
assertFalse(dc.isConnected());// not connected
DatagramSocket s1 = dc.socket();
- assertSocketBeforeConnect(s1);
+
+ assertFalse(s1.isBound());
+ assertFalse(s1.isClosed());
+ assertFalse(s1.isConnected());
+ assertFalse(s1.getBroadcast());
+ assertFalse(s1.getReuseAddress());
+ assertNull(s1.getInetAddress());
+ assertTrue(s1.getLocalAddress().isAnyLocalAddress());
+ assertEquals(s1.getLocalPort(), 0);
+ assertNull(s1.getLocalSocketAddress());
+ assertEquals(s1.getPort(), -1);
+ assertTrue(s1.getReceiveBufferSize() >= 8192);
+ assertNull(s1.getRemoteSocketAddress());
+ assertFalse(s1.getReuseAddress());
+ assertTrue(s1.getSendBufferSize() >= 8192);
+ assertEquals(s1.getSoTimeout(), 0);
+ assertEquals(s1.getTrafficClass(), 0);
+
DatagramSocket s2 = dc.socket();
// same
assertSame(s1, s2);
+
+ dc.close();
}
/**
* Test method for 'DatagramChannelImpl.socket()'
- *
- * @throws IOException
*/
public void testSocket_Block_BasicStatusAfterConnect() throws IOException {
final DatagramChannel dc = DatagramChannel.open();
@@ -285,10 +301,11 @@
DatagramSocket s2 = dc.socket();
// same
assertSame(s1, s2);
+
+ dc.close();
}
- public void testSocket_NonBlock_BasicStatusAfterConnect()
- throws IOException {
+ public void testSocket_NonBlock_BasicStatusAfterConnect() throws IOException {
final DatagramChannel dc = DatagramChannel.open();
dc.connect(datagramSocket1Address);
dc.configureBlocking(false);
@@ -298,60 +315,11 @@
DatagramSocket s2 = dc.socket();
// same
assertSame(s1, s2);
+
+ dc.close();
}
- /**
- * Test method for 'DatagramChannelImpl.socket()'
- *
- * @throws IOException
- */
- public void testSocket_ActionsBeforeConnect() throws IOException {
- assertFalse(this.channel1.isConnected());// not connected
- DatagramSocket s = this.channel1.socket();
- assertSocketActionBeforeConnect(s);
- }
-
- /**
- * Test method for 'DatagramChannelImpl.socket()'
- *
- * @throws IOException
- */
- public void testSocket_Block_ActionsAfterConnect() throws IOException {
- assertFalse(this.channel1.isConnected());// not connected
- this.channel1.connect(datagramSocket1Address);
- DatagramSocket s = this.channel1.socket();
- assertSocketActionAfterConnect(s);
- }
-
- public void testSocket_NonBlock_ActionsAfterConnect() throws IOException {
- this.channel1.connect(datagramSocket1Address);
- this.channel1.configureBlocking(false);
- DatagramSocket s = this.channel1.socket();
- assertSocketActionAfterConnect(s);
- }
-
- private void assertSocketBeforeConnect(DatagramSocket s)
- throws SocketException {
- assertFalse(s.isBound());
- assertFalse(s.isClosed());
- assertFalse(s.isConnected());
- assertFalse(s.getBroadcast());
- assertFalse(s.getReuseAddress());
- assertNull(s.getInetAddress());
- assertTrue(s.getLocalAddress().isAnyLocalAddress());
- assertEquals(s.getLocalPort(), 0);
- assertNull(s.getLocalSocketAddress());
- assertEquals(s.getPort(), -1);
- assertTrue(s.getReceiveBufferSize() >= 8192);
- assertNull(s.getRemoteSocketAddress());
- assertFalse(s.getReuseAddress());
- assertTrue(s.getSendBufferSize() >= 8192);
- assertEquals(s.getSoTimeout(), 0);
- assertEquals(s.getTrafficClass(), 0);
- }
-
- private void assertSocketAfterConnect(DatagramSocket s)
- throws SocketException {
+ private void assertSocketAfterConnect(DatagramSocket s) throws SocketException {
assertTrue(s.isBound());
assertFalse(s.isClosed());
assertTrue(s.isConnected());
@@ -369,25 +337,52 @@
assertEquals(s.getTrafficClass(), 0);
}
- private void assertSocketActionBeforeConnect(DatagramSocket s)
- throws IOException {
+ /**
+ * Test method for 'DatagramChannelImpl.socket()'
+ */
+ public void testSocket_ActionsBeforeConnect() throws IOException {
+ assertFalse(channel1.isConnected());// not connected
+ assertTrue(channel1.isBlocking());
+ DatagramSocket s = channel1.socket();
+
s.connect(datagramSocket2Address);
- assertFalse(this.channel1.isConnected());
- assertFalse(s.isConnected());
+ assertTrue(channel1.isConnected());
+ assertTrue(s.isConnected());
s.disconnect();
- assertFalse(this.channel1.isConnected());
+ assertFalse(channel1.isConnected());
assertFalse(s.isConnected());
s.close();
assertTrue(s.isClosed());
- assertFalse(this.channel1.isOpen());
+ assertFalse(channel1.isOpen());
}
- private void assertSocketActionAfterConnect(DatagramSocket s)
- throws IOException {
+ /**
+ * Test method for 'DatagramChannelImpl.socket()'
+ */
+ public void testSocket_Block_ActionsAfterConnect() throws IOException {
+ assertFalse(this.channel1.isConnected());// not connected
+ this.channel1.connect(datagramSocket1Address);
+ DatagramSocket s = this.channel1.socket();
+ assertSocketActionAfterConnect(s);
+ }
+
+ public void testSocket_NonBlock_ActionsAfterConnect() throws IOException {
+ this.channel1.connect(datagramSocket1Address);
+ this.channel1.configureBlocking(false);
+ DatagramSocket s = this.channel1.socket();
+ assertSocketActionAfterConnect(s);
+ }
+
+ private void assertSocketActionAfterConnect(DatagramSocket s) throws IOException {
assertEquals(s.getPort(), datagramSocket1Address.getPort());
- s.connect(datagramSocket2Address);
+ try {
+ s.connect(datagramSocket2Address);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
assertTrue(this.channel1.isConnected());
assertTrue(s.isConnected());
// not changed
@@ -408,8 +403,6 @@
/**
* Test method for 'DatagramChannelImpl.isConnected()'
- *
- * @throws IOException
*/
public void testIsConnected_WithServer() throws IOException {
connectLocalServer();
@@ -444,8 +437,6 @@
/**
* Test method for 'DatagramChannelImpl.connect(SocketAddress)'
- *
- * @throws IOException
*/
public void testConnect_NonBlockWithServer() throws IOException {
// Non blocking mode
@@ -457,8 +448,6 @@
/**
* Test method for 'DatagramChannelImpl.connect(SocketAddress)'
- *
- * @throws IOException
*/
public void testConnect_Null() throws IOException {
assertFalse(this.channel1.isConnected());
@@ -472,8 +461,6 @@
/**
* Test method for 'DatagramChannelImpl.connect(SocketAddress)'
- *
- * @throws IOException
*/
public void testConnect_UnsupportedType() throws IOException {
assertFalse(this.channel1.isConnected());
@@ -495,8 +482,6 @@
/**
* Test method for 'DatagramChannelImpl.connect(SocketAddress)'
- *
- * @throws IOException
*/
public void testConnect_Unresolved() throws IOException {
assertFalse(this.channel1.isConnected());
@@ -520,9 +505,6 @@
/**
* Test method for 'DatagramChannelImpl.connect(SocketAddress)'
- *
- * @throws IOException
- *
*/
public void testConnect_ClosedChannelException() throws IOException {
assertFalse(this.channel1.isConnected());
@@ -538,9 +520,6 @@
/**
* Test method for 'DatagramChannelImpl.connect(SocketAddress)'
- *
- * @throws IOException
- *
*/
public void testConnect_IllegalStateException() throws IOException {
assertFalse(this.channel1.isConnected());
@@ -557,9 +536,6 @@
/**
* Test method for 'DatagramChannelImpl.connect(SocketAddress)'
- *
- * @throws IOException
- *
*/
public void testConnect_CheckOpenBeforeStatus() throws IOException {
assertFalse(this.channel1.isConnected());
@@ -611,8 +587,6 @@
/**
* Test method for 'DatagramChannelImpl.disconnect()'
- *
- * @throws IOException
*/
public void testDisconnect_BeforeConnect() throws IOException {
assertFalse(this.channel1.isConnected());
@@ -622,8 +596,6 @@
/**
* Test method for 'DatagramChannelImpl.disconnect()'
- *
- * @throws IOException
*/
public void testDisconnect_UnconnectedClosed() throws IOException {
assertFalse(this.channel1.isConnected());
@@ -635,8 +607,6 @@
/**
* Test method for 'DatagramChannelImpl.disconnect()'
- *
- * @throws IOException
*/
public void testDisconnect_BlockWithServerChannelClosed()
throws IOException {
@@ -649,8 +619,6 @@
/**
* Test method for 'DatagramChannelImpl.disconnect()'
- *
- * @throws IOException
*/
public void testDisconnect_NonBlockWithServerChannelClosed()
throws IOException {
@@ -663,8 +631,6 @@
/**
* Test method for 'DatagramChannelImpl.disconnect()'
- *
- * @throws IOException
*/
public void testDisconnect_BlockWithServerServerClosed() throws IOException {
assertTrue(this.channel1.isBlocking());
@@ -678,8 +644,6 @@
/**
* Test method for 'DatagramChannelImpl.disconnect()'
- *
- * @throws IOException
*/
public void testDisconnect_NonBlockWithServerServerClosed()
throws IOException {
@@ -699,8 +663,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_UnconnectedNull() throws Exception {
assertFalse(this.channel1.isConnected());
@@ -714,8 +676,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_UnconnectedReadonly() throws Exception {
assertFalse(this.channel1.isConnected());
@@ -732,8 +692,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_UnconnectedBufEmpty() throws Exception {
this.channel1.configureBlocking(false);
@@ -749,6 +707,8 @@
assertFalse(dc.socket().isBound());
ByteBuffer dst = ByteBuffer.allocateDirect(CAPACITY_ZERO);
assertNull(dc.receive(dst));
+
+ dc.close();
}
public void testReceive_UnboundBufNotEmpty() throws Exception {
@@ -761,6 +721,8 @@
dst.put((byte) 88);
assertEquals(dst.position() + CAPACITY_NORMAL - 1, dst.limit());
assertNull(dc.receive(dst));
+
+ dc.close();
}
public void testReceive_UnboundBufFull() throws Exception {
@@ -774,12 +736,12 @@
dst.put((byte) 88);
assertEquals(dst.position(), dst.limit());
assertNull(dc.receive(dst));
+
+ dc.close();
}
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_UnconnectedClose() throws Exception {
assertFalse(this.channel1.isConnected());
@@ -796,8 +758,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_UnconnectedCloseNull() throws Exception {
assertFalse(this.channel1.isConnected());
@@ -814,8 +774,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_UnconnectedCloseReadonly() throws Exception {
assertFalse(this.channel1.isConnected());
@@ -834,8 +792,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerBufEmpty() throws Exception {
this.channel1.configureBlocking(false);
@@ -844,8 +800,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_BlockNoServerNull() throws Exception {
assertTrue(this.channel1.isBlocking());
@@ -854,8 +808,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerNull() throws Exception {
this.channel1.configureBlocking(false);
@@ -864,8 +816,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_BlockNoServerReadonly() throws Exception {
assertTrue(this.channel1.isBlocking());
@@ -874,8 +824,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerReadonly() throws Exception {
this.channel1.configureBlocking(false);
@@ -884,8 +832,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerBufZero() throws Exception {
this.channel1.configureBlocking(false);
@@ -894,8 +840,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerBufNotEmpty() throws Exception {
this.channel1.configureBlocking(false);
@@ -907,8 +851,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerBufFull() throws Exception {
this.channel1.configureBlocking(false);
@@ -919,8 +861,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_BlockNoServerChannelClose() throws Exception {
assertTrue(this.channel1.isBlocking());
@@ -929,8 +869,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerChannelClose() throws Exception {
this.channel1.configureBlocking(false);
@@ -939,8 +877,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_BlockNoServerCloseNull() throws Exception {
assertTrue(this.channel1.isBlocking());
@@ -949,8 +885,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerCloseNull() throws Exception {
this.channel1.configureBlocking(false);
@@ -959,8 +893,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_NonBlockNoServerCloseReadonly() throws Exception {
this.channel1.configureBlocking(false);
@@ -969,8 +901,6 @@
/**
* Test method for 'DatagramChannelImpl.receive(ByteBuffer)'
- *
- * @throws Exception
*/
public void testReceive_BlockNoServerCloseReadonly() throws Exception {
assertTrue(this.channel1.isBlocking());
@@ -1325,8 +1255,7 @@
Thread.sleep(TIME_UNIT);
channel2.send(ByteBuffer.wrap(str.getBytes()), datagramSocket1Address);
fail("Should throw SocketException!");
- } catch (SocketException e) {
- //expected
+ } catch (SocketException expected) {
}
}
@@ -1345,7 +1274,7 @@
InetSocketAddress expectedAddress, String expectedString) throws IOException {
try {
ByteBuffer buf = ByteBuffer.wrap(new byte[bufSize]);
- InetSocketAddress senderAddr = null;
+ InetSocketAddress senderAddr;
long startTime = System.currentTimeMillis();
do {
senderAddr = (InetSocketAddress) this.channel1.receive(buf);
@@ -2014,7 +1943,7 @@
sourceArray[i] = (byte) i;
}
- this.channel1.connect(channel1.socket().getLocalSocketAddress());
+ this.channel1.connect(channel1.getLocalAddress());
this.channel2.connect(datagramSocket1Address); // the different addr
// write
@@ -2044,11 +1973,13 @@
assertEquals(CAPACITY_NORMAL, dc.write(sourceBuf));
// Connect channel2 after data has been written.
- channel2.connect(dc.socket().getLocalSocketAddress());
+ channel2.connect(dc.getLocalAddress());
// read
ByteBuffer targetBuf = ByteBuffer.wrap(targetArray);
closeBlockedReaderChannel2(targetBuf);
+
+ dc.close();
}
// NOTE: The original harmony test tested that things still work
@@ -2234,11 +2165,13 @@
assertEquals(CAPACITY_NORMAL, dc.write(sourceBuf));
// Connect channel2 after data has been written.
- channel2.connect(dc.socket().getLocalSocketAddress());
+ channel2.connect(dc.getLocalAddress());
// read
ByteBuffer targetBuf = ByteBuffer.wrap(targetArray);
assertEquals(0, this.channel2.read(targetBuf));
+
+ dc.close();
}
// NOTE: The original harmony test tested that things still work
@@ -2333,7 +2266,7 @@
readBuf[1] = ByteBuffer.allocateDirect(CAPACITY_NORMAL);
channel1.configureBlocking(true);
- assertEquals(CAPACITY_NORMAL, channel1.read(readBuf,0,2));
+ assertEquals(CAPACITY_NORMAL, channel1.read(readBuf, 0, 2));
}
/**
@@ -2443,13 +2376,15 @@
} catch (IllegalBlockingModeException e) {
// expected
}
+
+ channel.close();
}
public void test_bounded_harmony6493() throws IOException {
DatagramChannel server = DatagramChannel.open();
InetSocketAddress addr = new InetSocketAddress("localhost", 0);
- server.socket().bind(addr);
- SocketAddress boundedAddress = server.socket().getLocalSocketAddress();
+ server.bind(addr);
+ SocketAddress boundedAddress = server.getLocalAddress();
DatagramChannel client = DatagramChannel.open();
ByteBuffer sent = ByteBuffer.allocate(1024);
@@ -2461,4 +2396,146 @@
server.close();
client.close();
}
+
+ public void test_bind_null() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ assertNull(dc.getLocalAddress());
+
+ dc.bind(null);
+
+ InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress();
+ assertTrue(localAddress.getAddress().isAnyLocalAddress());
+ assertTrue(localAddress.getPort() > 0);
+ } finally {
+ dc.close();
+ }
+ }
+
+ public void test_bind_failure() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ // Bind to a local address that is in use
+ dc.bind(channel1Address);
+ fail();
+ } catch (IOException expected) {
+ } finally {
+ dc.close();
+ }
+ }
+
+ public void test_bind_closed() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.close();
+
+ try {
+ dc.bind(null);
+ fail();
+ } catch (ClosedChannelException expected) {
+ } finally {
+ dc.close();
+ }
+ }
+
+ public void test_bind_twice() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(null);
+
+ try {
+ dc.bind(null);
+ fail();
+ } catch (AlreadyBoundException expected) {
+ } finally {
+ dc.close();
+ }
+ }
+
+ public void test_bind_explicitPort() throws Exception {
+ InetSocketAddress address = (InetSocketAddress) channel1.getLocalAddress();
+ assertTrue(address.getPort() > 0);
+
+ DatagramChannel dc = DatagramChannel.open();
+ // Allow the socket to bind to a port we know is already in use.
+ dc.socket().setReuseAddress(true);
+ InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort());
+ dc.bind(bindAddress);
+
+ InetSocketAddress boundAddress = (InetSocketAddress) dc.getLocalAddress();
+ assertEquals(bindAddress.getHostName(), boundAddress.getHostName());
+ assertEquals(bindAddress.getPort(), boundAddress.getPort());
+
+ dc.close();
+ channel1.close();
+ }
+
+ /** Checks that the SocketChannel and associated Socket agree on the socket state. */
+ public void test_bind_socketSync() throws IOException {
+ DatagramChannel dc = DatagramChannel.open();
+ assertNull(dc.getLocalAddress());
+
+ DatagramSocket socket = dc.socket();
+ assertNull(socket.getLocalSocketAddress());
+ assertFalse(socket.isBound());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ dc.bind(bindAddr);
+
+ InetSocketAddress actualAddr = (InetSocketAddress) dc.getLocalAddress();
+ assertEquals(actualAddr, socket.getLocalSocketAddress());
+ assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+ assertTrue(socket.isBound());
+ assertFalse(socket.isConnected());
+ assertFalse(socket.isClosed());
+
+ dc.close();
+
+ assertFalse(dc.isOpen());
+ assertTrue(socket.isClosed());
+ }
+
+ /**
+ * Checks that the SocketChannel and associated Socket agree on the socket state, even if
+ * the Socket object is requested/created after bind().
+ */
+ public void test_bind_socketSyncAfterBind() throws IOException {
+ DatagramChannel dc = DatagramChannel.open();
+ assertNull(dc.getLocalAddress());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ dc.bind(bindAddr);
+
+ // Socket creation after bind().
+ DatagramSocket socket = dc.socket();
+ InetSocketAddress actualAddr = (InetSocketAddress) dc.getLocalAddress();
+ assertEquals(actualAddr, socket.getLocalSocketAddress());
+ assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+ assertTrue(socket.isBound());
+ assertFalse(socket.isConnected());
+ assertFalse(socket.isClosed());
+
+ dc.close();
+
+ assertFalse(dc.isOpen());
+ assertTrue(socket.isClosed());
+ }
+
+ public void test_getLocalSocketAddress_afterClose() throws IOException {
+ DatagramChannel dc = DatagramChannel.open();
+ assertNull(dc.getLocalAddress());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ dc.bind(bindAddr);
+
+ assertNotNull(dc.getLocalAddress());
+
+ dc.close();
+
+ assertFalse(dc.isOpen());
+
+ try {
+ dc.getLocalAddress();
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java
index 990badc..ccf5a14 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileChannelTest.java
@@ -786,6 +786,7 @@
// shared lock, but it works on Windows & Linux.
assertTrue(fileLock.isShared());
assertSame(readOnlyFileChannel, fileLock.channel());
+ assertSame(readOnlyFileChannel, fileLock.acquiredBy());
assertEquals(POSITION, fileLock.position());
assertEquals(SIZE, fileLock.size());
}
@@ -800,6 +801,7 @@
assertTrue(fileLock.isValid());
assertFalse(fileLock.isShared());
assertSame(writeOnlyFileChannel, fileLock.channel());
+ assertSame(writeOnlyFileChannel, fileLock.acquiredBy());
assertEquals(POSITION, fileLock.position());
assertEquals(SIZE, fileLock.size());
}
@@ -816,6 +818,7 @@
assertEquals(POSITION, fileLock.position());
assertEquals(SIZE, fileLock.size());
assertSame(readOnlyFileChannel, fileLock.channel());
+ assertSame(readOnlyFileChannel, fileLock.acquiredBy());
}
/**
@@ -994,6 +997,7 @@
// shared lock, but it works on Windows & Linux.
assertTrue(fileLock.isShared());
assertSame(readOnlyFileChannel, fileLock.channel());
+ assertSame(readOnlyFileChannel, fileLock.acquiredBy());
assertEquals(POSITION, fileLock.position());
assertEquals(SIZE, fileLock.size());
}
@@ -1008,6 +1012,7 @@
assertTrue(fileLock.isValid());
assertFalse(fileLock.isShared());
assertSame(writeOnlyFileChannel, fileLock.channel());
+ assertSame(writeOnlyFileChannel, fileLock.acquiredBy());
assertEquals(POSITION, fileLock.position());
assertEquals(SIZE, fileLock.size());
}
@@ -1024,6 +1029,7 @@
assertEquals(POSITION, fileLock.position());
assertEquals(SIZE, fileLock.size());
assertSame(readOnlyFileChannel, fileLock.channel());
+ assertSame(readOnlyFileChannel, fileLock.acquiredBy());
}
/**
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileLockTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileLockTest.java
index 7e3b671..c4d372b 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileLockTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/FileLockTest.java
@@ -75,6 +75,7 @@
public void test_Constructor_Ljava_nio_channels_FileChannelJJZ() {
FileLock fileLock1 = new MockFileLock(null, 0, 0, false);
assertNull(fileLock1.channel());
+ assertNull(fileLock1.acquiredBy());
try {
new MockFileLock(readWriteChannel, -1, 0, false);
@@ -107,6 +108,15 @@
}
/**
+ * @tests java.nio.channels.FileLock#acquiredBy()
+ */
+ public void test_acquiredBy() {
+ assertSame(readWriteChannel, mockLock.acquiredBy());
+ FileLock lock = new MockFileLock(null, 0, 10, true);
+ assertNull(lock.acquiredBy());
+ }
+
+ /**
* @tests java.nio.channels.FileLock#position()
*/
public void test_position() {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockDatagramChannel.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockDatagramChannel.java
index 43b146e..41388ec 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockDatagramChannel.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockDatagramChannel.java
@@ -30,50 +30,62 @@
super(arg0);
}
+ @Override
public DatagramSocket socket() {
return null;
}
+ @Override
public boolean isConnected() {
return false;
}
+ @Override
public DatagramChannel connect(SocketAddress arg0) throws IOException {
return null;
}
+ @Override
public DatagramChannel disconnect() throws IOException {
return null;
}
+ @Override
public SocketAddress receive(ByteBuffer arg0) throws IOException {
return null;
}
+ @Override
public int send(ByteBuffer arg0, SocketAddress arg1) throws IOException {
return 0;
}
+ @Override
public int read(ByteBuffer arg0) throws IOException {
return 0;
}
+ @Override
public long read(ByteBuffer[] arg0, int arg1, int arg2) throws IOException {
return 0;
}
+ @Override
public int write(ByteBuffer arg0) throws IOException {
return 0;
}
+ @Override
public long write(ByteBuffer[] arg0, int arg1, int arg2) throws IOException {
return 0;
}
+ @Override
protected void implCloseSelectableChannel() throws IOException {
// empty
}
+ @Override
protected void implConfigureBlocking(boolean arg0) throws IOException {
// empty
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockServerSocketChannel.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockServerSocketChannel.java
index af090b6..783d2ce 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockServerSocketChannel.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockServerSocketChannel.java
@@ -29,17 +29,21 @@
super(arg0);
}
+ @Override
public ServerSocket socket() {
return null;
}
+ @Override
public SocketChannel accept() throws IOException {
return null;
}
+ @Override
protected void implCloseSelectableChannel() throws IOException {
}
+ @Override
protected void implConfigureBlocking(boolean arg0) throws IOException {
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockSocketChannel.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockSocketChannel.java
index beab939..47dad9d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockSocketChannel.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/MockSocketChannel.java
@@ -30,45 +30,56 @@
super(arg0);
}
+ @Override
public Socket socket() {
return null;
}
+ @Override
public boolean isConnected() {
return false;
}
+ @Override
public boolean isConnectionPending() {
return false;
}
+ @Override
public boolean connect(SocketAddress arg0) throws IOException {
return false;
}
+ @Override
public boolean finishConnect() throws IOException {
return false;
}
+ @Override
public int read(ByteBuffer arg0) throws IOException {
return 0;
}
+ @Override
public long read(ByteBuffer[] arg0, int arg1, int arg2) throws IOException {
return 0;
}
+ @Override
public int write(ByteBuffer arg0) throws IOException {
return 0;
}
+ @Override
public long write(ByteBuffer[] arg0, int arg1, int arg2) throws IOException {
return 0;
}
+ @Override
protected void implCloseSelectableChannel() throws IOException {
}
+ @Override
protected void implConfigureBlocking(boolean arg0) throws IOException {
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
index 828ab30..b417adc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
@@ -24,6 +24,7 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.IllegalBlockingModeException;
@@ -108,6 +109,157 @@
}
// -------------------------------------------------------------------
+ // Tests for bind()
+ // -------------------------------------------------------------------
+
+ public void test_bind_null() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ try {
+ assertNull(ssc.getLocalAddress());
+
+ ssc.bind(null);
+
+ InetSocketAddress localAddress = (InetSocketAddress) ssc.getLocalAddress();
+ assertTrue(localAddress.getAddress().isAnyLocalAddress());
+ assertTrue(localAddress.getPort() > 0);
+ } finally {
+ ssc.close();
+ }
+ }
+
+ public void test_bind_failure() throws Exception {
+ ServerSocketChannel portHog = ServerSocketChannel.open();
+ portHog.bind(null);
+
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ try {
+ // Bind to a local address that is in use
+ ssc.bind(portHog.getLocalAddress());
+ fail();
+ } catch (IOException expected) {
+ } finally {
+ ssc.close();
+ portHog.close();
+ }
+ }
+
+ public void test_bind_closed() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.close();
+
+ try {
+ ssc.bind(null);
+ fail();
+ } catch (ClosedChannelException expected) {
+ } finally {
+ ssc.close();
+ }
+ }
+
+ public void test_bind_twice() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.bind(null);
+
+ try {
+ ssc.bind(null);
+ fail();
+ } catch (AlreadyBoundException expected) {
+ } finally {
+ ssc.close();
+ }
+ }
+
+ public void test_bind_explicitPort() throws Exception {
+ ServerSocketChannel portPickingChannel = ServerSocketChannel.open();
+ // Have the OS find a free port.
+ portPickingChannel.bind(null);
+
+ InetSocketAddress address = (InetSocketAddress) portPickingChannel.getLocalAddress();
+ assertTrue(address.getPort() > 0);
+ portPickingChannel.close();
+
+ // There is a risk of flakiness here if the port is allocated to something else between
+ // close() and bind().
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort());
+ ssc.bind(bindAddress);
+
+ InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress();
+ assertEquals(bindAddress.getHostName(), boundAddress.getHostName());
+ assertEquals(bindAddress.getPort(), boundAddress.getPort());
+
+ ssc.close();
+ }
+
+ public void test_bind_socketSync() throws IOException {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ assertNull(ssc.getLocalAddress());
+
+ ServerSocket socket = ssc.socket();
+ assertNull(socket.getLocalSocketAddress());
+ assertFalse(socket.isBound());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ ssc.bind(bindAddr);
+
+ InetSocketAddress actualAddr = (InetSocketAddress) ssc.getLocalAddress();
+ assertEquals(actualAddr, socket.getLocalSocketAddress());
+ assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+ assertTrue(socket.isBound());
+ assertFalse(socket.isClosed());
+
+ ssc.close();
+
+ assertFalse(ssc.isOpen());
+ assertTrue(socket.isClosed());
+ }
+
+ public void test_bind_socketSyncAfterBind() throws IOException {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ assertNull(ssc.getLocalAddress());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ ssc.bind(bindAddr);
+
+ // Socket creation after bind().
+ ServerSocket socket = ssc.socket();
+ InetSocketAddress actualAddr = (InetSocketAddress) ssc.getLocalAddress();
+ assertEquals(actualAddr, socket.getLocalSocketAddress());
+ assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+ assertTrue(socket.isBound());
+ assertFalse(socket.isClosed());
+
+ ssc.close();
+
+ assertFalse(ssc.isOpen());
+ assertTrue(socket.isClosed());
+ }
+
+ // -------------------------------------------------------------------
+ // Test for getLocalSocketAddress()
+ // -------------------------------------------------------------------
+
+ public void test_getLocalSocketAddress_afterClose() throws IOException {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ assertNull(ssc.getLocalAddress());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ ssc.bind(bindAddr);
+
+ assertNotNull(ssc.getLocalAddress());
+
+ ssc.close();
+
+ assertFalse(ssc.isOpen());
+
+ try {
+ ssc.getLocalAddress();
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
+
+ // -------------------------------------------------------------------
// Test for socket()
// -------------------------------------------------------------------
@@ -235,8 +387,7 @@
public void testAccept_Block_NoConnect() throws IOException {
assertTrue(this.serverChannel.isBlocking());
- ServerSocket gotSocket = this.serverChannel.socket();
- gotSocket.bind(null);
+ serverChannel.bind(null);
// blocking mode , will block and wait for ever...
// so must close the server channel with another thread.
new Thread() {
@@ -259,8 +410,7 @@
}
public void testAccept_NonBlock_NoConnect() throws IOException {
- ServerSocket gotSocket = this.serverChannel.socket();
- gotSocket.bind(null);
+ this.serverChannel.bind(null);
this.serverChannel.configureBlocking(false);
// non-blocking mode , will immediately return
assertNull(this.serverChannel.accept());
@@ -270,13 +420,13 @@
* @tests ServerSocketChannel#accept().socket()
*/
public void test_read_Blocking_RealData() throws IOException {
- serverChannel.socket().bind(null);
+ serverChannel.bind(null);
ByteBuffer buf = ByteBuffer.allocate(CAPACITY_NORMAL);
for (int i = 0; i < CAPACITY_NORMAL; i++) {
buf.put((byte) i);
}
- clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+ clientChannel.connect(serverChannel.getLocalAddress());
Socket serverSocket = serverChannel.accept().socket();
InputStream in = serverSocket.getInputStream();
buf.flip();
@@ -309,13 +459,13 @@
*/
public void test_read_NonBlocking_RealData() throws Exception {
serverChannel.configureBlocking(false);
- serverChannel.socket().bind(null);
+ serverChannel.bind(null);
ByteBuffer buf = ByteBuffer.allocate(CAPACITY_NORMAL);
for (int i = 0; i < CAPACITY_NORMAL; i++) {
buf.put((byte) i);
}
buf.flip();
- clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+ clientChannel.connect(serverChannel.getLocalAddress());
Socket serverSocket = serverChannel.accept().socket();
InputStream in = serverSocket.getInputStream();
clientChannel.write(buf);
@@ -328,14 +478,13 @@
*/
public void test_write_Blocking_RealData() throws IOException {
assertTrue(serverChannel.isBlocking());
- ServerSocket serverSocket = serverChannel.socket();
- serverSocket.bind(null);
+ serverChannel.bind(null);
byte[] writeContent = new byte[CAPACITY_NORMAL];
for (int i = 0; i < writeContent.length; i++) {
writeContent[i] = (byte) i;
}
- clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+ clientChannel.connect(serverChannel.getLocalAddress());
Socket socket = serverChannel.accept().socket();
OutputStream out = socket.getOutputStream();
out.write(writeContent);
@@ -350,14 +499,13 @@
*/
public void test_write_NonBlocking_RealData() throws Exception {
serverChannel.configureBlocking(false);
- ServerSocket serverSocket = serverChannel.socket();
- serverSocket.bind(null);
+ serverChannel.bind(null);
byte[] writeContent = new byte[CAPACITY_NORMAL];
for (int i = 0; i < CAPACITY_NORMAL; i++) {
writeContent[i] = (byte) i;
}
- clientChannel.connect(serverSocket.getLocalSocketAddress());
+ clientChannel.connect(serverChannel.getLocalAddress());
Socket clientSocket = serverChannel.accept().socket();
OutputStream out = clientSocket.getOutputStream();
out.write(writeContent);
@@ -371,13 +519,13 @@
*/
public void test_read_LByteBuffer_Blocking_ReadWriteRealLargeData()
throws IOException, InterruptedException {
- serverChannel.socket().bind(null);
+ serverChannel.bind(null);
ByteBuffer buf = ByteBuffer.allocate(CAPACITY_64KB);
for (int i = 0; i < CAPACITY_64KB; i++) {
buf.put((byte) i);
}
buf.flip();
- clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+ clientChannel.connect(serverChannel.getLocalAddress());
WriteChannelThread writeThread = new WriteChannelThread(clientChannel, buf);
writeThread.start();
Socket socket = serverChannel.accept().socket();
@@ -416,13 +564,13 @@
public void test_read_LByteBuffer_NonBlocking_ReadWriteRealLargeData()
throws Exception {
serverChannel.configureBlocking(false);
- serverChannel.socket().bind(null);
+ serverChannel.bind(null);
ByteBuffer buf = ByteBuffer.allocate(CAPACITY_64KB);
for (int i = 0; i < CAPACITY_64KB; i++) {
buf.put((byte) i);
}
buf.flip();
- clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+ clientChannel.connect(serverChannel.getLocalAddress());
WriteChannelThread writeThread = new WriteChannelThread(clientChannel, buf);
writeThread.start();
Socket socket = serverChannel.accept().socket();
@@ -441,12 +589,12 @@
public void test_write_LByteBuffer_NonBlocking_ReadWriteRealLargeData()
throws Exception {
serverChannel.configureBlocking(false);
- serverChannel.socket().bind(null);
+ serverChannel.bind(null);
byte[] writeContent = new byte[CAPACITY_64KB];
for (int i = 0; i < writeContent.length; i++) {
writeContent[i] = (byte) i;
}
- clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+ clientChannel.connect(serverChannel.getLocalAddress());
Socket socket = serverChannel.accept().socket();
WriteSocketThread writeThread = new WriteSocketThread(socket, writeContent);
writeThread.start();
@@ -484,12 +632,12 @@
*/
public void test_write_LByteBuffer_Blocking_ReadWriteRealLargeData()
throws Exception {
- serverChannel.socket().bind(null);
+ serverChannel.bind(null);
byte[] writeContent = new byte[CAPACITY_64KB];
for (int i = 0; i < writeContent.length; i++) {
writeContent[i] = (byte) i;
}
- clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+ clientChannel.connect(serverChannel.getLocalAddress());
Socket socket = serverChannel.accept().socket();
WriteSocketThread writeThread = new WriteSocketThread(socket, writeContent);
writeThread.start();
@@ -531,9 +679,9 @@
final int SO_TIMEOUT = 10;
ServerSocketChannel sc = ServerSocketChannel.open();
try {
- ServerSocket ss = sc.socket();
- ss.bind(null);
+ sc.bind(null);
sc.configureBlocking(false);
+ ServerSocket ss = sc.socket();
ss.setSoTimeout(SO_TIMEOUT);
SocketChannel client = sc.accept();
// non blocking mode, returns null since there are no pending connections.
@@ -556,15 +704,13 @@
try {
gotSocket.accept();
fail("Should throw an IllegalBlockingModeException");
- } catch (IllegalBlockingModeException e) {
- // expected
+ } catch (IllegalBlockingModeException expected) {
}
serverChannel.close();
try {
gotSocket.accept();
fail("Should throw an IllegalBlockingModeException");
- } catch (IllegalBlockingModeException e) {
- // expected
+ } catch (IllegalBlockingModeException expected) {
}
}
@@ -578,15 +724,13 @@
try {
gotSocket.accept();
fail("Should throw an IllegalBlockingModeException");
- } catch (IllegalBlockingModeException e) {
- // expected
+ } catch (IllegalBlockingModeException expected) {
}
serverChannel.close();
try {
gotSocket.accept();
fail("Should throw an IllegalBlockingModeException");
- } catch (IllegalBlockingModeException e) {
- // expected
+ } catch (IllegalBlockingModeException expected) {
}
}
@@ -596,20 +740,18 @@
public void test_socket_accept_Nonblocking_Bound() throws IOException {
// regression test for Harmony-748
serverChannel.configureBlocking(false);
+ serverChannel.bind(null);
ServerSocket gotSocket = serverChannel.socket();
- gotSocket.bind(null);
try {
gotSocket.accept();
fail("Should throw an IllegalBlockingModeException");
- } catch (IllegalBlockingModeException e) {
- // expected
+ } catch (IllegalBlockingModeException expected) {
}
serverChannel.close();
try {
gotSocket.accept();
fail("Should throw a ClosedChannelException");
- } catch (ClosedChannelException e) {
- // expected
+ } catch (ClosedChannelException expected) {
}
}
@@ -619,22 +761,20 @@
public void test_socket_accept_Blocking_Bound() throws IOException {
// regression test for Harmony-748
serverChannel.configureBlocking(true);
- ServerSocket gotSocket = serverChannel.socket();
- gotSocket.bind(null);
+ serverChannel.bind(null);
serverChannel.close();
try {
- gotSocket.accept();
+ serverChannel.socket().accept();
fail("Should throw a ClosedChannelException");
- } catch (ClosedChannelException e) {
- // expected
+ } catch (ClosedChannelException expected) {
}
}
/**
* Regression test for HARMONY-4961
*/
public void test_socket_getLocalPort() throws IOException {
- serverChannel.socket().bind(null);
- clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+ serverChannel.bind(null);
+ clientChannel.connect(serverChannel.getLocalAddress());
SocketChannel myChannel = serverChannel.accept();
int port = myChannel.socket().getLocalPort();
assertEquals(serverChannel.socket().getLocalPort(), port);
@@ -648,7 +788,7 @@
*/
public void test_accept_configureBlocking() throws Exception {
InetSocketAddress localAddr = new InetSocketAddress("localhost", 0);
- serverChannel.socket().bind(localAddr);
+ serverChannel.bind(localAddr);
// configure the channel non-blocking
// when it is accepting in main thread
@@ -667,8 +807,7 @@
try {
serverChannel.accept();
fail("should throw AsynchronousCloseException");
- } catch (AsynchronousCloseException e) {
- // expected
+ } catch (AsynchronousCloseException expected) {
}
serverChannel.close();
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java
index 1151292..52dff79 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java
@@ -30,6 +30,7 @@
import java.net.SocketException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ConnectionPendingException;
@@ -145,14 +146,93 @@
* Test method for 'java.nio.channels.SocketChannel.open(SocketAddress)'
*/
public void testOpenSocketAddress_Null() throws IOException {
- SocketChannel channel1IP = null;
try {
- channel1IP = SocketChannel.open(null);
+ SocketChannel.open(null);
fail("Should throw an IllegalArgumentException");
} catch (IllegalArgumentException e) {
// correct
}
- assertNull(channel1IP);
+ }
+
+ public void testBind_Null() throws Exception {
+ assertNull(channel1.getLocalAddress());
+
+ channel1.bind(null);
+
+ InetSocketAddress localAddress = (InetSocketAddress) channel1.getLocalAddress();
+ assertTrue(localAddress.getAddress().isAnyLocalAddress());
+ assertTrue(localAddress.getPort() > 0);
+ }
+
+ public void testBind_Failure() throws Exception {
+ assertNull(channel1.getLocalAddress());
+
+ try {
+ // Bind to a local address that is in use
+ channel1.bind(localAddr1);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testBind_Closed() throws Exception {
+ channel1.close();
+
+ try {
+ channel1.bind(null);
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
+
+ public void testBind_Twice() throws Exception {
+ channel1.bind(null);
+
+ try {
+ channel1.bind(null);
+ fail();
+ } catch (AlreadyBoundException expected) {
+ }
+ }
+
+ public void testBind_explicitPort() throws Exception {
+ ServerSocketChannel portPickingChannel = ServerSocketChannel.open();
+ // Have the OS find a free port.
+ portPickingChannel.bind(null);
+ InetSocketAddress address = (InetSocketAddress) portPickingChannel.getLocalAddress();
+ assertTrue(address.getPort() > 0);
+ portPickingChannel.close();
+
+ // There is a risk of flakiness here if the port is allocated to something else between
+ // close() and bind().
+ InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort());
+ // Allow the socket to bind to a port we know is already in use.
+ channel1.socket().setReuseAddress(true);
+ channel1.bind(bindAddress);
+
+ InetSocketAddress boundAddress = (InetSocketAddress) channel1.getLocalAddress();
+ assertEquals(bindAddress.getHostName(), boundAddress.getHostName());
+ assertEquals(bindAddress.getPort(), boundAddress.getPort());
+ }
+
+ public void test_getLocalSocketAddress_afterClose() throws IOException {
+ SocketChannel sc = SocketChannel.open();
+ assertNull(sc.getLocalAddress());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ sc.bind(bindAddr);
+
+ assertNotNull(sc.getLocalAddress());
+
+ sc.close();
+
+ assertFalse(sc.isOpen());
+
+ try {
+ sc.getLocalAddress();
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
}
/*
@@ -178,8 +258,7 @@
} catch (NotYetConnectedException e) {
// correct
}
- long readNum = CAPACITY_NORMAL;
- readNum = testMSChannel.read(byteBuf);
+ long readNum = testMSChannel.read(byteBuf);
assertEquals(0, readNum);
readNum = CAPACITY_NORMAL;
readNum = testMSChannelnull.read(byteBuf);
@@ -273,6 +352,7 @@
public void testSocket_BasicStatusBeforeConnect() throws IOException {
assertFalse(this.channel1.isConnected());// not connected
Socket s1 = this.channel1.socket();
+ assertSocketBeforeBind(s1);
assertSocketBeforeConnect(s1);
Socket s2 = this.channel1.socket();
// same
@@ -296,12 +376,15 @@
assertFalse(this.channel1.isConnected());// not connected
this.channel1.configureBlocking(false);
boolean connected = channel1.connect(localAddr1);
- Socket s1 = null;
- Socket s2 = null;
+ Socket s1;
+ Socket s2;
if (!connected) {
assertFalse(this.channel1.isConnected());
assertTrue(this.channel1.isConnectionPending());
s1 = this.channel1.socket();
+ // A connect() causes an implicit bind()
+ assertSocketAfterImplicitBind(s1);
+
// status of not connected
assertSocketBeforeConnect(s1);
s2 = this.channel1.socket();
@@ -429,8 +512,26 @@
assertTrue(socket.getLocalPort() != -1);
}
- private void assertSocketBeforeConnect(Socket s) throws IOException {
+ private void assertSocketBeforeBind(Socket s) {
assertFalse(s.isBound());
+ assertTrue(s.getLocalAddress().isAnyLocalAddress());
+ // RI fails here. RI returns 0 while spec says unbound socket should
+ // return -1.
+ assertEquals(-1, s.getLocalPort());
+ assertNull(s.getLocalSocketAddress());
+ }
+
+ private void assertSocketAfterImplicitBind(Socket s) throws IOException {
+ assertTrue(s.isBound());
+ assertTrue(s.getLocalAddress().isLoopbackAddress());
+ assertTrue(s.getLocalPort() > 0);
+
+ InetSocketAddress localSocketAddress = (InetSocketAddress) s.getLocalSocketAddress();
+ assertTrue(localSocketAddress.getAddress().isLoopbackAddress());
+ assertEquals(s.getLocalPort(), localSocketAddress.getPort());
+ }
+
+ private void assertSocketBeforeConnect(Socket s) throws IOException {
assertFalse(s.isClosed());
assertFalse(s.isConnected());
assertFalse(s.getKeepAlive());
@@ -454,12 +555,7 @@
assertFalse(s.isOutputShutdown());
assertNull(s.getInetAddress());
- assertEquals(s.getLocalAddress().getHostAddress(), "0.0.0.0");
- // RI fails here. RI returns 0 while spec says unbound socket should
- // return -1.
- assertEquals(-1, s.getLocalPort());
assertFalse(s.getReuseAddress());
- assertNull(s.getLocalSocketAddress());
// not connected
assertEquals(0, s.getPort());
@@ -1620,8 +1716,6 @@
public void testCFII_Data_FinishConnect_AddrSetServerStartLater()
throws IOException, InterruptedException {
ensureServerClosed();
- java.nio.ByteBuffer[] writeBufArr = new java.nio.ByteBuffer[1];
- writeBufArr[0] = java.nio.ByteBuffer.allocate(CAPACITY_NORMAL);
this.channel1.configureBlocking(false);
try {
SocketChannel.open(localAddr1);
@@ -1690,8 +1784,6 @@
public void testCFII_Data_FinishConnect_ServerStartLater()
throws IOException {
ensureServerClosed();
- java.nio.ByteBuffer[] writeBufArr = new java.nio.ByteBuffer[1];
- writeBufArr[0] = java.nio.ByteBuffer.allocate(CAPACITY_NORMAL);
this.channel1.configureBlocking(true);
try {
this.channel1.finishConnect();
@@ -1790,7 +1882,7 @@
ServerSocket serversocket = theServerChannel.socket();
serversocket.setReuseAddress(true);
// Bind the socket
- serversocket.bind(address);
+ theServerChannel.bind(address);
boolean doneNonBlockingConnect = false;
// Loop so that we make sure we're definitely testing finishConnect()
@@ -1842,7 +1934,7 @@
ByteBuffer readContent = ByteBuffer.allocate(CAPACITY_NORMAL + 1);
int totalCount = 0;
- int count = 0;
+ int count;
long startTime = System.currentTimeMillis();
// use SocketChannel.read to read data
while (totalCount <= CAPACITY_NORMAL) {
@@ -1886,7 +1978,7 @@
channel1.configureBlocking(false);
ByteBuffer readContent = ByteBuffer.allocate(CAPACITY_NORMAL + 1);
int totalCount = 0;
- int count = 0;
+ int count;
long startTime = System.currentTimeMillis();
// use SocketChannel.read to read data
while (totalCount <= CAPACITY_NORMAL) {
@@ -2111,8 +2203,7 @@
ByteBuffer buffer = ByteBuffer.allocateDirect(128);
ServerSocketChannel server = ServerSocketChannel.open();
- server.socket().bind(
- new InetSocketAddress(InetAddress.getLocalHost(), 0), 5);
+ server.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0), 5);
Socket client = new Socket(InetAddress.getLocalHost(), server.socket()
.getLocalPort());
client.setTcpNoDelay(false);
@@ -2266,7 +2357,7 @@
// note: blocking-mode will make the read process endless!
this.channel1.configureBlocking(false);
try {
- channel1.read(null, 0, 0);
+ channel1.read(null, 0, 1);
fail("Should throw NPE");
} catch (NullPointerException e) {
// correct
@@ -2275,7 +2366,7 @@
if (tryFinish()) {
try {
- channel1.read(null, 0, 0);
+ channel1.read(null, 0, 1);
fail("Should throw NPE");
} catch (NullPointerException e) {
// correct
@@ -2606,7 +2697,7 @@
channel1.configureBlocking(isBlocking);
long startTime = System.currentTimeMillis();
long totalRead = 0;
- long countRead = 0;
+ long countRead;
while (totalRead <= CAPACITY_NORMAL * 2) {
countRead = channel1.read(readContents, 0, 2);
@@ -2686,7 +2777,7 @@
InputStream in = acceptedSocket.getInputStream();
byte[] readContent = new byte[CAPACITY_NORMAL * 2 + 1];
int totalCount = 0;
- int count = 0;
+ int count;
// if the channel could not finish reading in TIMEOUT ms, the test
// fails. It is used to guarantee the test never hangs even if there
// are bugs of SocketChannel implementation.
@@ -2730,9 +2821,9 @@
*/
public void test_writev() throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(null);
+ ssc.bind(null);
SocketChannel sc = SocketChannel.open();
- sc.connect(ssc.socket().getLocalSocketAddress());
+ sc.connect(ssc.getLocalAddress());
SocketChannel sock = ssc.accept();
ByteBuffer[] buf = { ByteBuffer.allocate(10), ByteBuffer.allocateDirect(20) };
@@ -2757,10 +2848,10 @@
public void test_writev2() throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
- ssc.socket().bind(null);
+ ssc.bind(null);
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
- boolean connected = sc.connect(ssc.socket().getLocalSocketAddress());
+ boolean connected = sc.connect(ssc.getLocalAddress());
SocketChannel sock = ssc.accept();
if (!connected) {
sc.finishConnect();
@@ -2795,10 +2886,10 @@
public void test_write$NonBlockingException() throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
- ssc.socket().bind(null);
+ ssc.bind(null);
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
- boolean connected = sc.connect(ssc.socket().getLocalSocketAddress());
+ boolean connected = sc.connect(ssc.getLocalAddress());
SocketChannel sock = ssc.accept();
if (!connected) {
sc.finishConnect();
@@ -2831,9 +2922,9 @@
public void test_write$LByteBuffer2() throws IOException {
// Set-up
ServerSocketChannel server = ServerSocketChannel.open();
- server.socket().bind(null);
+ server.bind(null);
SocketChannel client = SocketChannel.open();
- client.connect(server.socket().getLocalSocketAddress());
+ client.connect(server.getLocalAddress());
SocketChannel worker = server.accept();
// Test overlapping buffers
@@ -2847,7 +2938,7 @@
client.write(buffers);
client.close();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
- while (EOF != worker.read(readBuffer)) {};
+ while (EOF != worker.read(readBuffer)) {}
readBuffer.flip();
Buffer expected = ByteBuffer.allocate(1024).put(data).put(data).flip();
assertEquals(expected, readBuffer);
@@ -2863,9 +2954,9 @@
public void test_write$LByteBuffer_buffers() throws IOException {
// Set-up
ServerSocketChannel server = ServerSocketChannel.open();
- server.socket().bind(null);
+ server.bind(null);
SocketChannel client = SocketChannel.open();
- client.connect(server.socket().getLocalSocketAddress());
+ client.connect(server.getLocalAddress());
SocketChannel worker = server.accept();
// A variety of buffer types to write
@@ -2890,7 +2981,7 @@
client.write(buffers);
client.close();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
- while (EOF != worker.read(readBuffer)) {};
+ while (EOF != worker.read(readBuffer)) {}
readBuffer.flip();
assertEquals(ByteBuffer.wrap(data), readBuffer);
@@ -2905,9 +2996,9 @@
public void test_write$LByteBuffer_writes() throws IOException {
// Set-up
ServerSocketChannel server = ServerSocketChannel.open();
- server.socket().bind(null);
+ server.bind(null);
SocketChannel client = SocketChannel.open();
- client.connect(server.socket().getLocalSocketAddress());
+ client.connect(server.getLocalAddress());
SocketChannel worker = server.accept();
// Data to write
@@ -2932,7 +3023,7 @@
// Read what we wrote and check it
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
- while (EOF != worker.read(readBuffer)) {};
+ while (EOF != worker.read(readBuffer)) {}
readBuffer.flip();
assertEquals(ByteBuffer.wrap(data), readBuffer);
@@ -2947,10 +3038,10 @@
public void test_write$LByteBuffer_invalid() throws IOException {
// Set-up
ServerSocketChannel server = ServerSocketChannel.open();
- server.socket().bind(null);
+ server.bind(null);
SocketChannel client = SocketChannel.open();
- client.connect(server.socket().getLocalSocketAddress());
+ client.connect(server.getLocalAddress());
SocketChannel worker = server.accept();
@@ -2958,32 +3049,27 @@
try {
client.write((ByteBuffer[]) null);
fail("Should throw a NPE");
- } catch (NullPointerException e) {
- // expected
+ } catch (NullPointerException expected) {
}
try {
client.write((ByteBuffer[]) null, 0, 0);
fail("Should throw a NPE");
- } catch (NullPointerException e) {
- // expected
+ } catch (NullPointerException expected) {
}
try {
client.write((ByteBuffer[]) null, 1, 0);
fail("Should throw a NPE");
- } catch (NullPointerException e) {
- // expected
+ } catch (NullPointerException expected) {
}
try {
client.write((ByteBuffer[]) null, 0, 1);
fail("Should throw a NPE");
- } catch (NullPointerException e) {
- // expected
+ } catch (NullPointerException expected) {
}
try {
client.write((ByteBuffer[]) null, 1, 1);
fail("Should throw a NPE");
- } catch (NullPointerException e) {
- // expected
+ } catch (NullPointerException expected) {
}
ByteBuffer[] buffers = new ByteBuffer[1];
@@ -3055,9 +3141,9 @@
throws Exception {
// regression 1 for HARMONY-549
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(null);
+ ssc.bind(null);
SocketChannel sc = SocketChannel.open();
- sc.connect(ssc.socket().getLocalSocketAddress());
+ sc.connect(ssc.getLocalAddress());
ssc.accept().close();
ByteBuffer[] buf = { ByteBuffer.allocate(10) };
assertEquals(-1, sc.read(buf, 0, 1));
@@ -3071,16 +3157,15 @@
public void test_socketChannel_write_ByteBufferII() throws Exception {
// regression 2 for HARMONY-549
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(null);
+ ssc.bind(null);
SocketChannel sc = SocketChannel.open();
- sc.connect(ssc.socket().getLocalSocketAddress());
+ sc.connect(ssc.getLocalAddress());
SocketChannel sock = ssc.accept();
ByteBuffer[] buf = { ByteBuffer.allocate(10), null };
try {
sc.write(buf, 0, 2);
fail("should throw NPE");
- } catch (NullPointerException e) {
- // expected
+ } catch (NullPointerException expected) {
}
ssc.close();
sc.close();
@@ -3094,9 +3179,9 @@
public void test_socketChannel_read_ByteBufferII_bufNULL() throws Exception {
// regression 3 for HARMONY-549
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(null);
+ ssc.bind(null);
SocketChannel sc = SocketChannel.open();
- sc.connect(ssc.socket().getLocalSocketAddress());
+ sc.connect(ssc.getLocalAddress());
ssc.accept();
ByteBuffer[] buf = new ByteBuffer[2];
buf[0] = ByteBuffer.allocate(1);
@@ -3104,8 +3189,7 @@
try {
sc.read(buf, 0, 2);
fail("should throw NullPointerException");
- } catch (NullPointerException e) {
- // expected
+ } catch (NullPointerException expected) {
}
ssc.close();
sc.close();
@@ -3117,9 +3201,9 @@
public void test_socketChannel_write_close() throws Exception {
// regression 4 for HARMONY-549
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(null);
+ ssc.bind(null);
SocketChannel sc = SocketChannel.open();
- sc.connect(ssc.socket().getLocalSocketAddress());
+ sc.connect(ssc.getLocalAddress());
SocketChannel sock = ssc.accept();
ByteBuffer buf = null;
ssc.close();
@@ -3127,8 +3211,7 @@
try {
sc.write(buf);
fail("should throw NPE");
- } catch (NullPointerException e) {
- // expected
+ } catch (NullPointerException expected) {
}
sock.close();
}
@@ -3143,9 +3226,9 @@
ByteBuffer readBuf = ByteBuffer.allocate(11);
ByteBuffer buf = ByteBuffer.wrap(testStr.getBytes());
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(null);
+ ssc.bind(null);
SocketChannel sc = SocketChannel.open();
- sc.connect(ssc.socket().getLocalSocketAddress());
+ sc.connect(ssc.getLocalAddress());
buf.position(2);
ssc.accept().write(buf);
assertEquals(9, sc.read(readBuf));
@@ -3197,35 +3280,59 @@
fail();
} catch (IllegalBlockingModeException expected) {
}
+
try {
is.read(null);
fail();
} catch (NullPointerException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(buf, -1, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(buf, 0, -1);
fail();
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(buf, 0, 2);
fail();
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(buf, 2, 0);
fail();
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(null, 0, 0);
fail();
} catch (NullPointerException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
}
is.close();
@@ -3234,36 +3341,75 @@
is.read();
fail();
} catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
+ } catch (IOException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(null);
fail();
} catch (NullPointerException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
+ } catch (IOException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(buf, -1, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
+ } catch (IOException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(buf, 0, -1);
fail();
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
+ } catch (IOException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(buf, 0, 2);
fail();
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
+ } catch (IOException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(buf, 2, 0);
fail();
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
+ } catch (IOException expected) {
+ // Any of these exceptions are possible.
}
+
try {
is.read(null, 0, 0);
fail();
} catch (NullPointerException expected) {
+ // Any of these exceptions are possible.
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
+ } catch (IOException expected) {
+ // Any of these exceptions are possible.
}
}
@@ -3292,12 +3438,12 @@
} catch (IndexOutOfBoundsException expected) {
}
try {
- is.read(buf, 2, 0);
+ is.read(buf, 2, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
}
try {
- is.read(null, 0, 0);
+ is.read(null, 0, 1);
fail();
} catch (NullPointerException expected) {
}
@@ -3325,12 +3471,12 @@
} catch (IndexOutOfBoundsException expected) {
}
try {
- is.read(buf, 2, 0);
+ is.read(buf, 2, 1);
fail();
} catch (IndexOutOfBoundsException expected) {
}
try {
- is.read(null, 0, 0);
+ is.read(null, 0, 1);
fail();
} catch (NullPointerException expected) {
}
@@ -3350,32 +3496,55 @@
try {
os.write(null);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (NullPointerException expected) {
+ // Any of these exceptions are possible.
}
+
try {
os.write(buf, -1, 1);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
}
+
try {
os.write(buf, 0, -1);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
}
+
try {
os.write(buf, 0, 2);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
}
+
try {
- os.write(buf, 2, 0);
+ os.write(buf, 2, 1);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
}
+
try {
- os.write(null, 0, 0);
+ os.write(null, 0, 1);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (NullPointerException expected) {
+ // Any of these exceptions are possible.
}
os.close();
@@ -3385,35 +3554,59 @@
fail();
} catch (IllegalBlockingModeException expected) {
}
+
try {
os.write(null);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (NullPointerException expected) {
+ // Any of these exceptions are possible.
}
+
try {
os.write(buf, -1, 1);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
}
+
try {
os.write(buf, 0, -1);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
}
+
try {
os.write(buf, 0, 2);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
}
+
try {
os.write(buf, 2, 0);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (IndexOutOfBoundsException expected) {
+ // Any of these exceptions are possible.
}
+
try {
os.write(null, 0, 0);
fail();
+ } catch (IllegalBlockingModeException expected) {
+ // Any of these exceptions are possible.
} catch (NullPointerException expected) {
+ // Any of these exceptions are possible.
}
}
@@ -3554,40 +3747,47 @@
channel1.write(buffer);
}
- class MockSocketChannel extends SocketChannel{
+ class MockSocketChannel extends SocketChannel {
private boolean isWriteCalled = false;
private boolean isReadCalled = false;
- public MockSocketChannel(SelectorProvider provider){
+ public MockSocketChannel(SelectorProvider provider) {
super(provider);
}
+ @Override
public Socket socket() {
return null;
}
+ @Override
public boolean isConnected() {
return false;
}
+ @Override
public boolean isConnectionPending() {
return false;
}
+ @Override
public boolean connect(SocketAddress address) throws IOException {
return false;
}
+ @Override
public boolean finishConnect() throws IOException {
return false;
}
+ @Override
public int read(ByteBuffer target) throws IOException {
return 0;
}
+ @Override
public long read(ByteBuffer[] targets, int offset, int length) throws IOException {
// Verify that calling read(ByteBuffer[]) leads to the method
// read(ByteBuffer[], int, int) being called with a 0 for the
@@ -3598,10 +3798,12 @@
return 0;
}
+ @Override
public int write(ByteBuffer source) throws IOException {
return 0;
}
+ @Override
public long write(ByteBuffer[] sources, int offset, int length) throws IOException {
// Verify that calling write(ByteBuffer[]) leads to the method
// write(ByteBuffer[], int, int) being called with a 0 for the
@@ -3612,10 +3814,12 @@
return 0;
}
+ @Override
protected void implCloseSelectableChannel() throws IOException {
// empty
}
+ @Override
protected void implConfigureBlocking(boolean blockingMode) throws IOException {
// empty
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollationElementIteratorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollationElementIteratorTest.java
index dc0f4b7..0ca489c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollationElementIteratorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollationElementIteratorTest.java
@@ -66,6 +66,8 @@
int offset = iterator.getOffset();
assertEquals(text.length(), offset);
+
+ iterator.reset();
order = iterator.previous();
while (order != CollationElementIterator.NULLORDER) {
@@ -91,6 +93,8 @@
int offset = iterator.getOffset();
assertEquals(text.length(), offset);
+
+ iterator.reset();
order = iterator.previous();
while (order != CollationElementIterator.NULLORDER) {
@@ -176,19 +180,31 @@
RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(new Locale("es", "", "TRADITIONAL"));
String text = "cha";
CollationElementIterator iterator = rbColl.getCollationElementIterator(text);
+ iterator.setOffset(0);
+ assertEquals(0, iterator.getOffset());
iterator.setOffset(1);
- assertEquals(1, iterator.getOffset());
+ assertEquals(0, iterator.getOffset());
+ iterator.setOffset(2);
+ assertEquals(2, iterator.getOffset());
}
public void testSetTextString() {
RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(new Locale("es", "", "TRADITIONAL"));
String text = "caa";
CollationElementIterator iterator = rbColl.getCollationElementIterator(text);
+ iterator.setOffset(0);
+ assertEquals(0, iterator.getOffset());
iterator.setOffset(1);
assertEquals(1, iterator.getOffset());
+ iterator.setOffset(2);
+ assertEquals(2, iterator.getOffset());
iterator.setText("cha");
+ iterator.setOffset(0);
+ assertEquals(0, iterator.getOffset());
iterator.setOffset(1);
- assertEquals(1, iterator.getOffset());
+ assertEquals(0, iterator.getOffset());
+ iterator.setOffset(2);
+ assertEquals(2, iterator.getOffset());
}
public void testSetTextCharacterIterator() {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollatorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollatorTest.java
index 673fe87..9f5d141 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollatorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollatorTest.java
@@ -1,13 +1,13 @@
-/*
+/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
@@ -23,9 +23,6 @@
public class CollatorTest extends junit.framework.TestCase {
- /**
- * @tests java.text.Collator#clone()
- */
public void test_clone() {
Collator c = Collator.getInstance(Locale.GERMAN);
Collator c2 = (Collator) c.clone();
@@ -33,9 +30,6 @@
assertTrue("Clones were equivalent", c != c2);
}
- /**
- * @tests java.text.Collator#compare(java.lang.Object, java.lang.Object)
- */
public void test_compareLjava_lang_ObjectLjava_lang_Object() {
Collator c = Collator.getInstance(Locale.FRENCH);
Object o, o2;
@@ -119,9 +113,6 @@
fail("Failed to throw ClassCastException");
}
- /**
- * @tests java.text.Collator#equals(java.lang.Object)
- */
public void test_equalsLjava_lang_Object() {
Collator c = Collator.getInstance(Locale.ENGLISH);
Collator c2 = (Collator) c.clone();
@@ -130,9 +121,6 @@
assertTrue("Collators with different strengths equal", !c.equals(c2));
}
- /**
- * @tests java.text.Collator#equals(java.lang.String, java.lang.String)
- */
public void test_equalsLjava_lang_StringLjava_lang_String() {
Collator c = Collator.getInstance(Locale.FRENCH);
@@ -168,10 +156,6 @@
assertTrue("d) Failed on equivalence", c.equals("e", "e"));
}
- /**
- * @tests java.text.Collator#getAvailableLocales()
- */
- //FIXME This test fails on Harmony ClassLibrary
public void failing_test_getAvailableLocales() {
Locale[] locales = Collator.getAvailableLocales();
assertTrue("No locales", locales.length > 0);
@@ -199,10 +183,6 @@
assertTrue("Missing locales", english && german);
}
- /**
- * @tests java.text.Collator#getDecomposition()
- */
- //FIXME This test fails on Harmony ClassLibrary
public void failing_test_getDecomposition() {
RuleBasedCollator collator;
try {
@@ -215,40 +195,21 @@
collator.getDecomposition() == Collator.CANONICAL_DECOMPOSITION);
}
- /**
- * @tests java.text.Collator#getInstance()
- */
public void test_getInstance() {
Collator c1 = Collator.getInstance();
Collator c2 = Collator.getInstance(Locale.getDefault());
assertTrue("Wrong locale", c1.equals(c2));
}
- /**
- * @tests java.text.Collator#getInstance(java.util.Locale)
- */
public void test_getInstanceLjava_util_Locale() {
assertTrue("Used to test", true);
}
- /**
- * @tests java.text.Collator#getStrength()
- */
- public void test_getStrength() {
- RuleBasedCollator collator;
- try {
- collator = new RuleBasedCollator("; \u0300 < a, A < b < c < d");
- } catch (ParseException e) {
- fail("ParseException");
- return;
- }
+ public void test_getStrength() throws ParseException {
+ RuleBasedCollator collator = new RuleBasedCollator("&9 ; \u0300 < a, A < b < c < d");
assertTrue("Wrong default", collator.getStrength() == Collator.TERTIARY);
}
- /**
- * @tests java.text.Collator#setDecomposition(int)
- */
- //FIXME This test fails on Harmony ClassLibrary
public void failing_test_setDecompositionI() {
Collator c = Collator.getInstance(Locale.FRENCH);
c.setStrength(Collator.IDENTICAL);
@@ -267,9 +228,6 @@
"\u2163", "IV")); // roman number "IV"
}
- /**
- * @tests java.text.Collator#setStrength(int)
- */
public void test_setStrengthI() {
assertTrue("Used to test", true);
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/NumberFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/NumberFormatTest.java
index cb7ac4b..49e3d9e 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/NumberFormatTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/NumberFormatTest.java
@@ -174,16 +174,16 @@
format = (DecimalFormat) NumberFormat.getIntegerInstance(arLocale);
assertEquals(
"Test7: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).toPattern() returned wrong pattern",
- "#0;#0-", format.toPattern());
+ "#,##0", format.toPattern());
assertEquals(
"Test8: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).format(-35.76) returned wrong value",
- "\u0666-", format.format(-6));
+ "\u200f-\u0666", format.format(-6));
assertEquals(
"Test9: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).parse(\"-36-\") returned wrong number",
- new Long(-36), format.parse("36-"));
+ new Long(36), format.parse("36-"));
assertEquals(
"Test10: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).parseObject(\"36-\") returned wrong number",
- new Long(-36), format.parseObject("36-"));
+ new Long(36), format.parseObject("36-"));
assertEquals(
"Test11: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).getMaximumFractionDigits() returned wrong value",
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/RuleBasedCollatorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/RuleBasedCollatorTest.java
index 3e8f28d..f5a8057 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/RuleBasedCollatorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/RuleBasedCollatorTest.java
@@ -33,7 +33,7 @@
public void test_getCollationKeyLjava_lang_String() throws Exception {
// Regression test for HARMONY-28
String source = null;
- String Simple = "< a< b< c< d";
+ String Simple = "&9 < a< b< c< d";
RuleBasedCollator rbc = new RuleBasedCollator(Simple);
CollationKey ck = rbc.getCollationKey(source);
assertNull("Assert 1: getCollationKey (null) does not return null", ck);
@@ -41,13 +41,13 @@
public void testHashCode() throws ParseException {
{
- String rule = "< a < b < c < d";
+ String rule = "&9 < a < b < c < d";
RuleBasedCollator coll = new RuleBasedCollator(rule);
assertEquals(rule.hashCode(), coll.hashCode());
}
{
- String rule = "< a < b < c < d < e";
+ String rule = "&9 < a < b < c < d < e";
RuleBasedCollator coll = new RuleBasedCollator(rule);
assertEquals(rule.hashCode(), coll.hashCode());
}
@@ -63,7 +63,7 @@
}
public void testEqualsObject() throws ParseException {
- String rule = "< a < b < c < d < e";
+ String rule = "&9 < a < b < c < d < e";
RuleBasedCollator coll = new RuleBasedCollator(rule);
assertEquals(Collator.TERTIARY, coll.getStrength());
@@ -80,7 +80,7 @@
}
public void testCompareStringString() throws ParseException {
- String rule = "< c < b < a";
+ String rule = "&9 < c < b < a";
RuleBasedCollator coll = new RuleBasedCollator(rule);
assertEquals(-1, coll.compare("c", "a"));
}
@@ -98,7 +98,7 @@
}
public void testGetRules() throws ParseException {
- String rule = "< a = b < c";
+ String rule = "&9 < a = b < c";
RuleBasedCollator coll = new RuleBasedCollator(rule);
assertEquals(rule, coll.getRules());
}
@@ -139,7 +139,7 @@
}
//Regression for HARMONY-1352
try {
- new RuleBasedCollator("< a< b< c< d").getCollationElementIterator((String)null);
+ new RuleBasedCollator("&9 < a< b< c< d").getCollationElementIterator((String)null);
fail();
} catch (NullPointerException expected) {
}
@@ -184,7 +184,7 @@
}
//Regression for HARMONY-1352
try {
- new RuleBasedCollator("< a< b< c< d").getCollationElementIterator((CharacterIterator)null);
+ new RuleBasedCollator("&9 < a< b< c< d").getCollationElementIterator((CharacterIterator)null);
fail();
} catch (NullPointerException expected) {
}
@@ -244,20 +244,15 @@
public void testCompareNull() throws Exception {
//Regression for HARMONY-836
try {
- new RuleBasedCollator("< a").compare(null, null);
+ new RuleBasedCollator("&9 < a").compare(null, null);
fail();
} catch (NullPointerException expected) {
}
}
- public void testEmptyStringException() {
- //Regression for HARMONY-241
- try {
- new RuleBasedCollator("");
- fail();
- } catch (ParseException e) {
- assertEquals("java.text.ParseException", e.getClass().getName());
- assertEquals(0, e.getErrorOffset());
- }
+ public void testEmptyRules() throws Exception {
+ new RuleBasedCollator("");
+ new RuleBasedCollator(" ");
+ new RuleBasedCollator("# This is a comment.");
}
}
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 2c5f308..47c76f3 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
@@ -208,23 +208,23 @@
gc1 = new GregorianCalendar(AMERICA_NEW_YORK);
gc1.set(1999, Calendar.APRIL, 3, 16, 0); // day before DST change
gc1.add(Calendar.MILLISECOND, 24 * 60 * 60 * 1000);
- assertEquals("Wrong time after MILLISECOND change", 16, gc1
+ assertEquals("Wrong time after MILLISECOND change", 17, gc1
.get(Calendar.HOUR_OF_DAY));
gc1.set(1999, Calendar.APRIL, 3, 16, 0); // day before DST change
gc1.add(Calendar.SECOND, 24 * 60 * 60);
- assertEquals("Wrong time after SECOND change", 16, gc1
+ assertEquals("Wrong time after SECOND change", 17, gc1
.get(Calendar.HOUR_OF_DAY));
gc1.set(1999, Calendar.APRIL, 3, 16, 0); // day before DST change
gc1.add(Calendar.MINUTE, 24 * 60);
- assertEquals("Wrong time after MINUTE change", 16, gc1
+ assertEquals("Wrong time after MINUTE change", 17, gc1
.get(Calendar.HOUR_OF_DAY));
gc1.set(1999, Calendar.APRIL, 3, 16, 0); // day before DST change
gc1.add(Calendar.HOUR, 24);
- assertEquals("Wrong time after HOUR change", 16, gc1
+ assertEquals("Wrong time after HOUR change", 17, gc1
.get(Calendar.HOUR_OF_DAY));
gc1.set(1999, Calendar.APRIL, 3, 16, 0); // day before DST change
gc1.add(Calendar.HOUR_OF_DAY, 24);
- assertEquals("Wrong time after HOUR_OF_DAY change", 16, gc1
+ assertEquals("Wrong time after HOUR_OF_DAY change", 17, gc1
.get(Calendar.HOUR_OF_DAY));
gc1.set(1999, Calendar.APRIL, 3, 16, 0); // day before DST change
@@ -683,7 +683,7 @@
g = new GregorianCalendar(TimeZone.getTimeZone("Europe/London"),
new Locale("fr", "CA"));
minimalDaysInFirstWeek = g.getMinimalDaysInFirstWeek();
- assertEquals(4, minimalDaysInFirstWeek);
+ assertEquals(1, minimalDaysInFirstWeek);
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LocaleTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LocaleTest.java
index 426e52c..dfb2d96 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LocaleTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LocaleTest.java
@@ -175,7 +175,7 @@
// Regression for Harmony-1146
Locale l_countryCD = new Locale("", "CD");
- assertEquals("Congo [DRC]",
+ assertEquals("Congo (DRC)",
l_countryCD.getDisplayCountry());
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerTest.java
index 585ba44..cc6a0f0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ScannerTest.java
@@ -25,9 +25,9 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
@@ -39,12 +39,12 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
+import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
-import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
@@ -52,10 +52,9 @@
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
+import java.util.Scanner;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
-import java.util.Scanner;
-
import junit.framework.TestCase;
public class ScannerTest extends TestCase {
@@ -1058,27 +1057,20 @@
s.useLocale(Locale.CHINESE);
assertEquals(12300, s.nextInt(10));
- /*
- * There are three types of negative prefix all in all. '' '-' '(' There
- * are three types of negative suffix all in all. '' '-' ')' '(' and ')'
- * must be used togethor. Prefix '-' and suffix '-' must be used
- * exclusively.
- */
-
- /*
- * According to Integer regular expression: Integer :: = ( [-+]? (*
- * Numeral ) ) | LocalPositivePrefix Numeral LocalPositiveSuffix |
- * LocalNegativePrefix Numeral LocalNegativeSuffix 123- should be
- * recognized by scanner with locale ar_AE, (123) shouble be recognized
- * by scanner with locale mk_MK. But this is not the case on RI.
- */
- s = new Scanner("-123 123- -123-");
+ s = new Scanner("-123 123-");
s.useLocale(new Locale("ar", "AE"));
- assertEquals(-123, s.nextInt(10));
- // The following test case fails on RI
- assertEquals(-123, s.nextInt(10));
+ assertEquals(-123, s.nextInt());
try {
- s.nextInt(10);
+ s.nextInt();
+ fail();
+ } catch (InputMismatchException expected) {
+ }
+
+ s = new Scanner("-123 -123-");
+ s.useLocale(new Locale("ar", "AE"));
+ assertEquals(-123, s.nextInt());
+ try {
+ s.nextInt();
fail();
} catch (InputMismatchException expected) {
}
@@ -1244,24 +1236,17 @@
s.useLocale(Locale.CHINESE);
assertEquals(12300, s.nextInt());
- /*
- * There are three types of negative prefix all in all. '' '-' '(' There
- * are three types of negative suffix all in all. '' '-' ')' '(' and ')'
- * must be used togethor. Prefix '-' and suffix '-' must be used
- * exclusively.
- */
-
- /*
- * According to Integer regular expression: Integer :: = ( [-+]? (*
- * Numeral ) ) | LocalPositivePrefix Numeral LocalPositiveSuffix |
- * LocalNegativePrefix Numeral LocalNegativeSuffix 123- should be
- * recognized by scanner with locale ar_AE, (123) shouble be recognized
- * by scanner with locale mk_MK. But this is not the case on RI.
- */
- s = new Scanner("-123 123- -123-");
+ s = new Scanner("-123 123-");
s.useLocale(new Locale("ar", "AE"));
assertEquals(-123, s.nextInt());
- // The following test case fails on RI
+ try {
+ s.nextInt();
+ fail();
+ } catch (InputMismatchException expected) {
+ }
+
+ s = new Scanner("-123 -123-");
+ s.useLocale(new Locale("ar", "AE"));
assertEquals(-123, s.nextInt());
try {
s.nextInt();
@@ -3259,26 +3244,19 @@
assertTrue(s.hasNextInt(10));
assertEquals(12300, s.nextInt(10));
- /*
- * There are three types of negative prefix all in all. '' '-' '(' There
- * are three types of negative suffix all in all. '' '-' ')' '(' and ')'
- * must be used together. Prefix '-' and suffix '-' must be used
- * exclusively.
- */
-
- /*
- * According to Integer regular expression: Integer :: = ( [-+]? (*
- * Numeral ) ) | LocalPositivePrefix Numeral LocalPositiveSuffix |
- * LocalNegativePrefix Numeral LocalNegativeSuffix 123- should be
- * recognized by scanner with locale ar_AE, (123) should be recognized
- * by scanner with locale mk_MK. But this is not the case on RI.
- */
- s = new Scanner("-123 123- -123-");
+ s = new Scanner("-123 123-");
s.useLocale(new Locale("ar", "AE"));
assertTrue(s.hasNextInt(10));
assertEquals(-123, s.nextInt(10));
- // The following test case fails on RI
- assertTrue(s.hasNextInt(10));
+ assertFalse(s.hasNextInt(10));
+ try {
+ s.nextInt(10);
+ fail();
+ } catch (InputMismatchException expected) {
+ }
+
+ s = new Scanner("-123 -123-");
+ s.useLocale(new Locale("ar", "AE"));
assertEquals(-123, s.nextInt(10));
assertFalse(s.hasNextInt(10));
try {
@@ -3452,25 +3430,19 @@
assertTrue(s.hasNextInt());
assertEquals(12300, s.nextInt());
- /*
- * There are three types of negative prefix all in all. '' '-' '(' There
- * are three types of negative suffix all in all. '' '-' ')' '(' and ')'
- * must be used togethor. Prefix '-' and suffix '-' must be used
- * exclusively.
- */
-
- /*
- * According to Integer regular expression: Integer :: = ( [-+]? (*
- * Numeral ) ) | LocalPositivePrefix Numeral LocalPositiveSuffix |
- * LocalNegativePrefix Numeral LocalNegativeSuffix 123- should be
- * recognized by scanner with locale ar_AE, (123) shouble be recognized
- * by scanner with locale mk_MK. But this is not the case on RI.
- */
- s = new Scanner("-123 123- -123-");
+ s = new Scanner("-123 123-");
s.useLocale(new Locale("ar", "AE"));
assertTrue(s.hasNextInt());
assertEquals(-123, s.nextInt());
- // The following test case fails on RI
+ assertFalse(s.hasNextInt());
+ try {
+ s.nextInt();
+ fail();
+ } catch (InputMismatchException expected) {
+ }
+
+ s = new Scanner("-123 -123-");
+ s.useLocale(new Locale("ar", "AE"));
assertTrue(s.hasNextInt());
assertEquals(-123, s.nextInt());
assertFalse(s.hasNextInt());
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CheckedInputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CheckedInputStreamTest.java
index 1c8cc93..903f60b 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CheckedInputStreamTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CheckedInputStreamTest.java
@@ -18,28 +18,15 @@
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
-
import junit.framework.TestCase;
import tests.support.resource.Support_Resources;
public class CheckedInputStreamTest extends TestCase {
- @Override
- protected void tearDown() {
- try {
- File deletedFile = new File("empty.txt");
- deletedFile.delete();
- } catch (SecurityException e) {
- fail("Cannot delete file for security reasons");
- }
-
- }
-
/**
* java.util.zip.CheckedInputStream#CheckedInputStream(java.io.InputStream,
*java.util.zip.Checksum)
@@ -57,10 +44,8 @@
*/
public void test_getChecksum() throws Exception {
byte outBuf[] = new byte[100];
- // testing getChecksum for an empty file
- FileOutputStream outEmp = new FileOutputStream("empty.txt");
- outEmp.close();
- InputStream inEmp = new FileInputStream("empty.txt");
+ File f = File.createTempFile("CheckedInputStreamTest", ".txt");
+ InputStream inEmp = new FileInputStream(f);
CheckedInputStream checkEmpty = new CheckedInputStream(inEmp, new CRC32());
while (checkEmpty.read() >= 0) {
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CheckedOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CheckedOutputStreamTest.java
index e354872..09d5756 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CheckedOutputStreamTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/CheckedOutputStreamTest.java
@@ -32,7 +32,8 @@
public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_Checksum() {
// test method java.util.zip.checkedOutputStream.constructor
try {
- FileOutputStream outFile = new FileOutputStream("chkOut.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("CheckedOutputStreamTest", ".txt"));
CheckedOutputStream chkOut = new CheckedOutputStream(outFile,
new CRC32());
assertEquals("the checkSum value of the constructor is not 0", 0, chkOut
@@ -53,7 +54,8 @@
// test method java.util.zip.checkedOutputStream.getChecksum()
byte byteArray[] = { 1, 2, 3, 'e', 'r', 't', 'g', 3, 6 };
try {
- FileOutputStream outFile = new FileOutputStream("chkOut.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("CheckedOutputStreamTest", ".txt"));
CheckedOutputStream chkOut = new CheckedOutputStream(outFile,
new Adler32());
chkOut.write(byteArray[4]);
@@ -85,7 +87,8 @@
// test method java.util.zip.checkedOutputStream.writeI()
byte byteArray[] = { 1, 2, 3, 'e', 'r', 't', 'g', 3, 6 };
try {
- FileOutputStream outFile = new FileOutputStream("chkOut.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("CheckedOutputStreamTest", ".txt"));
CheckedOutputStream chkOut = new CheckedOutputStream(outFile,
new CRC32());
for (byte element : byteArray) {
@@ -109,7 +112,8 @@
// test method java.util.zip.checkOutputStream.writeBII()
byte byteArray[] = { 1, 2, 3, 'e', 'r', 't', 'g', 3, 6 };
try {
- FileOutputStream outFile = new FileOutputStream("chkOut.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("CheckedOutputStreamTest", ".txt"));
CheckedOutputStream chkOut = new CheckedOutputStream(outFile,
new CRC32());
chkOut.write(byteArray, 4, 5);
@@ -133,19 +137,4 @@
fail("Index for write is out of bounds");
}
}
-
- @Override
- protected void setUp() {
- }
-
- @Override
- protected void tearDown() {
- try {
- File deletedFile = new File("chkOut.txt");
- deletedFile.delete();
- } catch (SecurityException e) {
- fail("Cannot delete file for security reasons");
- }
- }
-
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterOutputStreamTest.java
index 185f7b2..e4be198 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterOutputStreamTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/DeflaterOutputStreamTest.java
@@ -60,7 +60,7 @@
}
}
- private byte outPutBuf[] = new byte[500];
+ private final byte outputBuf[] = new byte[500];
@Override
protected void setUp() {
@@ -70,11 +70,11 @@
Deflater deflate = new Deflater(1);
deflate.setInput(byteArray);
while (!(deflate.needsInput())) {
- x += deflate.deflate(outPutBuf, x, outPutBuf.length - x);
+ x += deflate.deflate(outputBuf, x, outputBuf.length - x);
}
deflate.finish();
while (!(deflate.finished())) {
- x = x + deflate.deflate(outPutBuf, x, outPutBuf.length - x);
+ x = x + deflate.deflate(outputBuf, x, outputBuf.length - x);
}
deflate.end();
}
@@ -85,7 +85,7 @@
*/
public void test_ConstructorLjava_io_OutputStreamLjava_util_zip_Deflater() throws Exception {
byte byteArray[] = { 1, 3, 4, 7, 8 };
- File f1 = new File("hyts_ConstruOD.tst");
+ File f1 = File.createTempFile("hyts_ConstruOD", ".tst");
FileOutputStream fos = new FileOutputStream(f1);
Deflater defl = null;
MyDeflaterOutputStream dos;
@@ -111,7 +111,7 @@
* java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream)
*/
public void test_ConstructorLjava_io_OutputStream() throws Exception {
- File f1 = new File("hyts_ConstruO.tst");
+ File f1 = File.createTempFile("hyts_ConstruO", ".tst");
FileOutputStream fos = new FileOutputStream(f1);
MyDeflaterOutputStream dos = new MyDeflaterOutputStream(fos);
@@ -119,7 +119,7 @@
// buffer.
assertEquals("Incorrect Buffer Size", 512, dos.getProtectedBuf().length);
- dos.write(outPutBuf);
+ dos.write(outputBuf);
dos.close();
f1.delete();
}
@@ -134,7 +134,7 @@
int negBuf = -5;
int zeroBuf = 0;
byte byteArray[] = { 1, 3, 4, 7, 8, 3, 6 };
- File f1 = new File("hyts_ConstruODI.tst");
+ File f1 = File.createTempFile("hyts_ConstruODI", ".tst");
FileOutputStream fos = new FileOutputStream(f1);
Deflater defl = null;
MyDeflaterOutputStream dos;
@@ -237,7 +237,7 @@
// Need test to see if method finish() actually finishes
// Only testing possible errors, not if it actually works
- File f1 = new File("finish.tst");
+ File f1 = File.createTempFile("finish", ".tst");
FileOutputStream fos1 = new FileOutputStream(f1);
DeflaterOutputStream dos = new DeflaterOutputStream(fos1);
byte byteArray[] = { 1, 3, 4, 6 };
@@ -285,7 +285,7 @@
* java.util.zip.DeflaterOutputStream#write(int)
*/
public void test_writeI() throws Exception {
- File f1 = new File("writeI1.tst");
+ File f1 = File.createTempFile("writeIL", ".tst");
FileOutputStream fos = new FileOutputStream(f1);
DeflaterOutputStream dos = new DeflaterOutputStream(fos);
for (int i = 0; i < 3; i++) {
@@ -324,7 +324,7 @@
byte byteArray[] = { 1, 3, 4, 7, 8, 3, 6 };
// Test to see if the correct bytes are saved.
- File f1 = new File("writeBII.tst");
+ File f1 = File.createTempFile("writeBII", ".tst");
FileOutputStream fos1 = new FileOutputStream(f1);
DeflaterOutputStream dos1 = new DeflaterOutputStream(fos1);
dos1.write(byteArray, 2, 3);
@@ -340,7 +340,7 @@
f1.delete();
// Test for trying to write more bytes than available from the array
- File f2 = new File("writeBII2.tst");
+ File f2 = File.createTempFile("writeBII2", ".tst");
FileOutputStream fos2 = new FileOutputStream(f2);
DeflaterOutputStream dos2 = new DeflaterOutputStream(fos2);
try {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPOutputStreamTest.java
index de54d5a..30a94f0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPOutputStreamTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/GZIPOutputStreamTest.java
@@ -47,7 +47,8 @@
*/
public void test_ConstructorLjava_io_OutputStream() {
try {
- FileOutputStream outFile = new FileOutputStream("GZIPOutCon.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("GZIPCon", ".txt"));
TestGZIPOutputStream outGZIP = new TestGZIPOutputStream(outFile);
assertNotNull("the constructor for GZIPOutputStream is null",
outGZIP);
@@ -66,7 +67,8 @@
*/
public void test_ConstructorLjava_io_OutputStreamI() {
try {
- FileOutputStream outFile = new FileOutputStream("GZIPOutCon.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("GZIPOutCon", ".txt"));
TestGZIPOutputStream outGZIP = new TestGZIPOutputStream(outFile,
100);
assertNotNull("the constructor for GZIPOutputStream is null",
@@ -87,7 +89,8 @@
// test method java.util.zip.GZIPOutputStream.finish()
byte byteArray[] = { 3, 5, 2, 'r', 'g', 'e', 'f', 'd', 'e', 'w' };
try {
- FileOutputStream outFile = new FileOutputStream("GZIPOutFinish.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("GZIPOutFinish", ".txt"));
TestGZIPOutputStream outGZIP = new TestGZIPOutputStream(outFile);
outGZIP.finish();
@@ -114,7 +117,8 @@
// test method java.util.zip.GZIPOutputStream.close()
byte byteArray[] = { 3, 5, 2, 'r', 'g', 'e', 'f', 'd', 'e', 'w' };
try {
- FileOutputStream outFile = new FileOutputStream("GZIPOutClose2.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("GZIPOutClose2", ".txt"));
TestGZIPOutputStream outGZIP = new TestGZIPOutputStream(outFile);
outGZIP.close();
int r = 0;
@@ -138,7 +142,8 @@
// test method java.util.zip.GZIPOutputStream.writeBII
byte byteArray[] = { 3, 5, 2, 'r', 'g', 'e', 'f', 'd', 'e', 'w' };
try {
- FileOutputStream outFile = new FileOutputStream("GZIPOutWrite.txt");
+ FileOutputStream outFile = new FileOutputStream(
+ File.createTempFile("GZIPOutWrite", ".txt"));
TestGZIPOutputStream outGZIP = new TestGZIPOutputStream(outFile);
outGZIP.write(byteArray, 0, 10);
// ran JDK and found this CRC32 value is 3097700292
@@ -179,26 +184,4 @@
assertEquals(2, in.read());
assertEquals(3, in.read());
}
-
- @Override
- protected void setUp() {
- }
-
- @Override
- protected void tearDown() {
-
- try {
- File dFile = new File("GZIPOutCon.txt");
- dFile.delete();
- File dFile2 = new File("GZIPOutFinish.txt");
- dFile2.delete();
- File dFile3 = new File("GZIPOutWrite.txt");
- dFile3.delete();
- File dFile4 = new File("GZIPOutClose2.txt");
- dFile4.delete();
- } catch (SecurityException e) {
- fail("Cannot delete file for security reasons");
- }
- }
-
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipEntryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipEntryTest.java
index 3b3d7d5..f034639 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipEntryTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipEntryTest.java
@@ -16,48 +16,25 @@
*/
package org.apache.harmony.tests.java.util.zip;
-import java.util.TimeZone;
-import java.util.zip.ZipEntry;
-import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-
+import java.util.TimeZone;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import libcore.io.Streams;
import tests.support.resource.Support_Resources;
public class ZipEntryTest extends junit.framework.TestCase {
-
- public byte[] getAllBytesFromStream(InputStream is) throws IOException {
- ByteArrayOutputStream bs = new ByteArrayOutputStream();
- byte[] buf = new byte[512];
- int iRead;
- int off;
- while (is.available() > 0) {
- iRead = is.read(buf, 0, buf.length);
- if (iRead > 0) bs.write(buf, 0, iRead);
- }
- return bs.toByteArray();
- }
-
// zip file hyts_ZipFile.zip must be included as a resource
- java.util.zip.ZipEntry zentry;
+ private ZipEntry zentry;
+ private ZipFile zfile;
- java.util.zip.ZipFile zfile;
-
- private static final String platformId = System.getProperty(
- "com.ibm.oti.configuration", "JDK")
- + System.getProperty("java.vm.version");
-
- static final String tempFileName = platformId + "zfzezi.zip";
-
- long orgSize;
-
- long orgCompressedSize;
-
- long orgCrc;
-
- long orgTime;
-
- String orgComment;
+ private long orgSize;
+ private long orgCompressedSize;
+ private long orgCrc;
+ private long orgTime;
/**
* java.util.zip.ZipEntry#ZipEntry(java.lang.String)
@@ -442,47 +419,26 @@
assertEquals(extraB.length, zeOutput.getExtra().length);
}
- /**
- * Sets up the fixture, for example, open a network connection. This method
- * is called before a test is executed.
- */
-
@Override
- protected void setUp() {
- java.io.File f = null;
- try {
- // Create a local copy of the file since some tests want to alter
- // information.
- f = new java.io.File(tempFileName);
- // Create absolute filename as ZipFile does not resolve using
- // user.dir
- f = new java.io.File(f.getAbsolutePath());
- f.delete();
- java.io.InputStream is = Support_Resources
- .getStream("hyts_ZipFile.zip");
- java.io.FileOutputStream fos = new java.io.FileOutputStream(f);
- byte[] rbuf = getAllBytesFromStream(is);
- fos.write(rbuf, 0, rbuf.length);
- is.close();
- fos.close();
- zfile = new java.util.zip.ZipFile(f);
- zentry = zfile.getEntry("File1.txt");
- orgSize = zentry.getSize();
- orgCompressedSize = zentry.getCompressedSize();
- orgCrc = zentry.getCrc();
- orgTime = zentry.getTime();
- orgComment = zentry.getComment();
- } catch (Exception e) {
- System.out.println("Exception during ZipFile setup <"
- + f.getAbsolutePath() + ">: ");
- e.printStackTrace();
- }
- }
+ protected void setUp() throws Exception {
+ // Create a local copy of the file since some tests want to alter
+ // information.
+ final File f = File.createTempFile("ZipEntryTest", ".zip");
+ InputStream is = Support_Resources.getStream("hyts_ZipFile.zip");
- /**
- * Tears down the fixture, for example, close a network connection. This
- * method is called after a test is executed.
- */
+ FileOutputStream fos = new java.io.FileOutputStream(f);
+ Streams.copy(is, fos);
+ is.close();
+ fos.close();
+
+ zfile = new ZipFile(f);
+ zentry = zfile.getEntry("File1.txt");
+
+ orgSize = zentry.getSize();
+ orgCompressedSize = zentry.getCompressedSize();
+ orgCrc = zentry.getCrc();
+ orgTime = zentry.getTime();
+ }
@Override
protected void tearDown() {
@@ -490,11 +446,8 @@
if (zfile != null) {
zfile.close();
}
- java.io.File f = new java.io.File(tempFileName);
- f.delete();
- } catch (java.io.IOException e) {
- System.out.println("Exception during tearDown");
+ } catch (IOException ignored) {
}
}
-
}
+
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipFileTest.java
index 0c3a4d5..5b96633 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipFileTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipFileTest.java
@@ -17,75 +17,37 @@
package org.apache.harmony.tests.java.util.zip;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.security.Permission;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
+import libcore.io.Streams;
import libcore.java.lang.ref.FinalizationTester;
import tests.support.resource.Support_Resources;
public class ZipFileTest extends junit.framework.TestCase {
- public byte[] getAllBytesFromStream(InputStream is) throws IOException {
- ByteArrayOutputStream bs = new ByteArrayOutputStream();
- byte[] buf = new byte[512];
- int iRead;
- int off;
- while (is.available() > 0) {
- iRead = is.read(buf, 0, buf.length);
- if (iRead > 0) bs.write(buf, 0, iRead);
- }
- return bs.toByteArray();
- }
-
// the file hyts_zipFile.zip in setup must be included as a resource
private String tempFileName;
-
private ZipFile zfile;
- // custom security manager
- SecurityManager sm = new SecurityManager() {
- final String forbidenPermissionAction = "read";
-
-
-
- public void checkPermission(Permission perm) {
- // only check if it's a FilePermission because Locale checks
- // for a PropertyPermission with action"read" to get system props.
- if (perm instanceof FilePermission
- && perm.getActions().equals(forbidenPermissionAction)) {
- throw new SecurityException();
- }
- }
- };
-
- /**
- * java.util.zip.ZipFile#ZipFile(java.io.File)
- */
- public void test_ConstructorLjava_io_File() {
- // Test for method java.util.zip.ZipFile(java.io.File)
- assertTrue("Used to test", true);
- }
-
/**
* java.util.zip.ZipFile#ZipFile(java.io.File, int)
*/
public void test_ConstructorLjava_io_FileI() throws IOException {
zfile.close(); // about to reopen the same temp file
+
File file = new File(tempFileName);
ZipFile zip = new ZipFile(file, ZipFile.OPEN_DELETE | ZipFile.OPEN_READ);
zip.close();
assertTrue("Zip should not exist", !file.exists());
+
file = new File(tempFileName);
- file.delete();
try {
zip = new ZipFile(file, ZipFile.OPEN_READ);
fail("IOException expected");
@@ -106,14 +68,12 @@
* java.util.zip.ZipFile#ZipFile(java.lang.String)
*/
public void test_ConstructorLjava_lang_String() throws IOException {
- System.setProperty("user.dir", System.getProperty("java.io.tmpdir"));
-
zfile.close(); // about to reopen the same temp file
ZipFile zip = new ZipFile(tempFileName);
zip.close();
File file = File.createTempFile("zip", "tmp");
try {
- zip = new ZipFile(file.getName());
+ zip = new ZipFile(file.getAbsolutePath());
fail("ZipException expected");
} catch (ZipException ee) {
// expected
@@ -441,10 +401,12 @@
// Create a local copy of the file since some tests want to alter information.
File tempFile = File.createTempFile("OldZipFileTest", "zip");
tempFileName = tempFile.getAbsolutePath();
+
+
InputStream is = Support_Resources.getStream("hyts_ZipFile.zip");
FileOutputStream fos = new FileOutputStream(tempFile);
- byte[] rbuf = getAllBytesFromStream(is);
- fos.write(rbuf, 0, rbuf.length);
+ Streams.copy(is, fos);
+
is.close();
fos.close();
zfile = new ZipFile(tempFile);
@@ -452,8 +414,6 @@
@Override
protected void tearDown() throws IOException {
- // Note zfile is a user-defined zip file used by other tests and
- // should not be deleted
if (zfile != null) {
zfile.close();
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java
index a9e43d1..7f42fa8 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java
@@ -41,13 +41,6 @@
* java.util.zip.ZipOutputStream#close()
*/
public void test_close() throws Exception {
- try {
- zos.close();
- fail("Close on empty stream failed to throw exception");
- } catch (ZipException e) {
- // expected
- }
-
zos = new ZipOutputStream(bos);
zos.putNextEntry(new ZipEntry("XX"));
zos.closeEntry();
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java
index 30a1a9c..31f5881 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java
@@ -28,23 +28,21 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
-
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
-
import junit.framework.TestCase;
-import dalvik.annotation.AndroidOnly;
-import dalvik.annotation.KnownFailure;
-
+import libcore.java.security.StandardNames;
/**
* Tests for SSLEngine class
*
*/
public class SSLEngineTest extends TestCase {
+ private static final int MAX_TLS_RECORD_SIZE = 32768;
private HandshakeHandler clientEngine;
private HandshakeHandler serverEngine;
@@ -56,9 +54,8 @@
/**
* Test for <code>SSLEngine()</code> constructor Assertion: creates
* SSLEngine object with null host and -1 port
- * @throws NoSuchAlgorithmException
*/
- public void test_Constructor() throws NoSuchAlgorithmException {
+ public void test_Constructor() throws Exception {
SSLEngine e = getEngine();
assertNull(e.getPeerHost());
assertEquals(-1, e.getPeerPort());
@@ -69,41 +66,31 @@
/**
* Test for <code>SSLEngine(String host, int port)</code> constructor
- * @throws NoSuchAlgorithmException
*/
- public void test_ConstructorLjava_lang_StringI01() throws NoSuchAlgorithmException {
+ public void test_ConstructorLjava_lang_StringI01() throws Exception {
int port = 1010;
SSLEngine e = getEngine(null, port);
assertNull(e.getPeerHost());
assertEquals(e.getPeerPort(), port);
try {
e.beginHandshake();
- } catch (IllegalStateException ex) {
- // expected
- } catch (SSLException ex) {
- fail("unexpected SSLException was thrown.");
+ fail("should throw IllegalStateException");
+ } catch (IllegalStateException expected) {
}
+
e = getEngine(null, port);
e.setUseClientMode(true);
- try {
- e.beginHandshake();
- } catch (SSLException ex) {
- // expected
- }
+ e.beginHandshake();
+
e = getEngine(null, port);
e.setUseClientMode(false);
- try {
- e.beginHandshake();
- } catch (SSLException ex) {
- // expected
- }
+ e.beginHandshake();
}
/**
* Test for <code>SSLEngine(String host, int port)</code> constructor
- * @throws NoSuchAlgorithmException
*/
- public void test_ConstructorLjava_lang_StringI02() throws NoSuchAlgorithmException {
+ public void test_ConstructorLjava_lang_StringI02() throws Exception {
String host = "new host";
int port = 8080;
SSLEngine e = getEngine(host, port);
@@ -118,9 +105,8 @@
/**
* Test for <code>getPeerHost()</code> method
- * @throws NoSuchAlgorithmException
*/
- public void test_getPeerHost() throws NoSuchAlgorithmException {
+ public void test_getPeerHost() throws Exception {
SSLEngine e = getEngine();
assertNull(e.getPeerHost());
e = getEngine("www.fortify.net", 80);
@@ -129,9 +115,8 @@
/**
* Test for <code>getPeerPort()</code> method
- * @throws NoSuchAlgorithmException
*/
- public void test_getPeerPort() throws NoSuchAlgorithmException {
+ public void test_getPeerPort() throws Exception {
SSLEngine e = getEngine();
assertEquals("Incorrect default value of peer port",
-1 ,e.getPeerPort());
@@ -139,92 +124,72 @@
assertEquals("Incorrect peer port", 80, e.getPeerPort());
}
- /**
- * @throws NoSuchAlgorithmException
- * javax.net.ssl.SSLEngine#getSupportedProtocols()
- */
- public void test_getSupportedProtocols() throws NoSuchAlgorithmException {
+ public void test_getSupportedProtocols() throws Exception {
SSLEngine sse = getEngine();
- try {
- String[] res = sse.getSupportedProtocols();
- assertNotNull(res);
- assertTrue(res.length > 0);
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+
+ String[] res = sse.getSupportedProtocols();
+ assertNotNull(res);
+ assertTrue(res.length > 0);
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#setEnabledProtocols(String[] protocols)
* javax.net.ssl.SSLEngine#getEnabledProtocols()
*/
- public void test_EnabledProtocols() throws NoSuchAlgorithmException {
+ public void test_EnabledProtocols() throws Exception {
SSLEngine sse = getEngine();
String[] pr = sse.getSupportedProtocols();
- try {
- sse.setEnabledProtocols(pr);
- String[] res = sse.getEnabledProtocols();
- assertNotNull("Null array was returned", res);
- assertEquals("Incorrect array length", res.length, pr.length);
- assertTrue("Incorrect array was returned", Arrays.equals(res, pr));
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+
+ sse.setEnabledProtocols(pr);
+ String[] res = sse.getEnabledProtocols();
+ assertNotNull("Null array was returned", res);
+ assertEquals("Incorrect array length", res.length, pr.length);
+ assertTrue("Incorrect array was returned", Arrays.equals(res, pr));
+
try {
sse.setEnabledProtocols(null);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
+ } catch (IllegalArgumentException expected) {
}
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#getSupportedCipherSuites()
*/
- public void test_getSupportedCipherSuites() throws NoSuchAlgorithmException {
+ public void test_getSupportedCipherSuites() throws Exception {
SSLEngine sse = getEngine();
- try {
- String[] res = sse.getSupportedCipherSuites();
- assertNotNull(res);
- assertTrue(res.length > 0);
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+
+ String[] res = sse.getSupportedCipherSuites();
+ assertNotNull(res);
+ assertTrue(res.length > 0);
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#setEnabledCipherSuites(String[] suites)
* javax.net.ssl.SSLEngine#getEnabledCipherSuites()
*/
- public void test_EnabledCipherSuites() throws NoSuchAlgorithmException {
+ public void test_EnabledCipherSuites() throws Exception {
SSLEngine sse = getEngine();
String[] st = sse.getSupportedCipherSuites();
- try {
- sse.setEnabledCipherSuites(st);
- String[] res = sse.getEnabledCipherSuites();
- assertNotNull("Null array was returned", res);
- assertEquals("Incorrect array length", res.length, st.length);
- assertTrue("Incorrect array was returned", Arrays.equals(res, st));
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+
+ sse.setEnabledCipherSuites(st);
+ String[] res = sse.getEnabledCipherSuites();
+ assertNotNull("Null array was returned", res);
+ assertEquals("Incorrect array length", res.length, st.length);
+ assertTrue("Incorrect array was returned", Arrays.equals(res, st));
+
try {
sse.setEnabledCipherSuites(null);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
+ } catch (IllegalArgumentException expected) {
}
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#setEnableSessionCreation(boolean flag)
* javax.net.ssl.SSLEngine#getEnableSessionCreation()
*/
- public void test_EnableSessionCreation() throws NoSuchAlgorithmException {
+ public void test_EnableSessionCreation() throws Exception {
SSLEngine sse = getEngine();
try {
assertTrue(sse.getEnableSessionCreation());
@@ -238,11 +203,10 @@
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#setNeedClientAuth(boolean need)
* javax.net.ssl.SSLEngine#getNeedClientAuth()
*/
- public void test_NeedClientAuth() throws NoSuchAlgorithmException {
+ public void test_NeedClientAuth() throws Exception {
SSLEngine sse = getEngine();
try {
sse.setNeedClientAuth(false);
@@ -255,137 +219,92 @@
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#setWantClientAuth(boolean want)
* javax.net.ssl.SSLEngine#getWantClientAuth()
*/
- public void test_WantClientAuth() throws NoSuchAlgorithmException {
+ public void test_WantClientAuth() throws Exception {
SSLEngine sse = getEngine();
- try {
- sse.setWantClientAuth(false);
- assertFalse(sse.getWantClientAuth());
- sse.setWantClientAuth(true);
- assertTrue(sse.getWantClientAuth());
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+ sse.setWantClientAuth(false);
+ assertFalse(sse.getWantClientAuth());
+ sse.setWantClientAuth(true);
+ assertTrue(sse.getWantClientAuth());
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#beginHandshake()
*/
- public void test_beginHandshake() throws NoSuchAlgorithmException {
+ public void test_beginHandshake() throws Exception {
SSLEngine sse = getEngine();
try {
sse.beginHandshake();
fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException se) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalStateException");
+ } catch (IllegalStateException expected) {
}
sse = getEngine("new host", 1080);
try {
sse.beginHandshake();
fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException ise) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalStateException");
+ } catch (IllegalStateException expected) {
}
sse = getEngine();
- try {
- sse.setUseClientMode(true);
- sse.beginHandshake();
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+ sse.setUseClientMode(true);
+ sse.beginHandshake();
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#setUseClientMode(boolean mode)
* javax.net.ssl.SSLEngine#getUseClientMode()
*/
- @AndroidOnly("The RI doesn't throw the expected IllegalStateException.")
- public void test_UseClientMode() throws NoSuchAlgorithmException {
+ public void test_UseClientMode() throws Exception {
SSLEngine sse = getEngine();
+ sse.setUseClientMode(false);
+ assertFalse(sse.getUseClientMode());
+ sse.setUseClientMode(true);
+ assertTrue(sse.getUseClientMode());
+
+ sse = getEngine(null, 1080);
+ sse.setUseClientMode(true);
+ sse.beginHandshake();
try {
sse.setUseClientMode(false);
- assertFalse(sse.getUseClientMode());
- sse.setUseClientMode(true);
- assertTrue(sse.getUseClientMode());
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
-
- try {
- sse = getEngine(null, 1080);
- sse.setUseClientMode(true);
- sse.beginHandshake();
- try {
- sse.setUseClientMode(false);
- fail("IllegalArgumentException was not thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- }
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
+ fail("IllegalArgumentException was not thrown");
+ } catch (IllegalArgumentException expected) {
}
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#getSession()
*/
- public void test_getSession() throws NoSuchAlgorithmException {
+ public void test_getSession() throws Exception {
SSLEngine sse = getEngine();
- try {
- assertNotNull(sse.getSession());
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+ assertNotNull(sse.getSession());
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#getHandshakeStatus()
*/
- public void test_getHandshakeStatus() throws NoSuchAlgorithmException {
+ public void test_getHandshakeStatus() throws Exception {
SSLEngine sse = getEngine();
- try {
- assertEquals(sse.getHandshakeStatus().toString(), "NOT_HANDSHAKING");
- sse.setUseClientMode(true);
- sse.beginHandshake();
- assertEquals(sse.getHandshakeStatus().toString(), "NEED_WRAP");
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+ assertEquals(sse.getHandshakeStatus().toString(), "NOT_HANDSHAKING");
+ sse.setUseClientMode(true);
+ sse.beginHandshake();
+ assertEquals(sse.getHandshakeStatus().toString(), "NEED_WRAP");
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#getDelegatedTask()
*/
- @KnownFailure("com.android.org.conscrypt.SSLEngineImpl#getDelegatedTask() throws NPE instead of returning null")
- public void test_getDelegatedTask() throws NoSuchAlgorithmException {
+ public void test_getDelegatedTask() throws Exception {
SSLEngine sse = getEngine();
- try {
- assertNull(sse.getDelegatedTask());
- } catch (Exception ex) {
- fail("Unexpected exception " + ex);
- }
+ assertNull(sse.getDelegatedTask());
}
/**
- * @throws IOException
- * @throws InterruptedException
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer[] dsts,
* int offset, int length)
* Exception case: SSLException should be thrown.
*/
- public void test_unwrap_01() throws IOException, InterruptedException {
+ public void test_unwrap_01() throws Exception {
prepareEngines();
doHandshake();
@@ -394,8 +313,7 @@
try {
clientEngine.engine.unwrap(bbs, new ByteBuffer[] { bbd }, 0, 1);
fail("SSLException wasn't thrown");
- } catch (SSLException ex) {
- //expected
+ } catch (SSLException expected) {
}
}
@@ -404,8 +322,7 @@
* int offset, int length)
* Exception case: IndexOutOfBoundsException should be thrown.
*/
- @KnownFailure("Fixed in DonutBurger, boundary checks missing")
- public void test_unwrap_02() throws SSLException {
+ public void test_unwrap_02() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer[] bbA = { ByteBuffer.allocate(100), ByteBuffer.allocate(10), ByteBuffer.allocate(100) };
@@ -417,26 +334,22 @@
try {
sse.unwrap(bb, bbA, -1, 3);
fail("IndexOutOfBoundsException wasn't thrown");
- } catch (IndexOutOfBoundsException iobe) {
- //expected
+ } catch (IndexOutOfBoundsException expected) {
}
try {
sse.unwrap(bb, bbA, 0, -3);
fail("IndexOutOfBoundsException wasn't thrown");
- } catch (IndexOutOfBoundsException iobe) {
- //expected
+ } catch (IndexOutOfBoundsException expected) {
}
try {
sse.unwrap(bb, bbA, bbA.length + 1, bbA.length);
fail("IndexOutOfBoundsException wasn't thrown");
- } catch (IndexOutOfBoundsException iobe) {
- //expected
+ } catch (IndexOutOfBoundsException expected) {
}
try {
sse.unwrap(bb, bbA, 0, bbA.length + 1);
fail("IndexOutOfBoundsException wasn't thrown");
- } catch (IndexOutOfBoundsException iobe) {
- //expected
+ } catch (IndexOutOfBoundsException expected) {
}
}
@@ -445,8 +358,7 @@
* int offset, int length)
* Exception case: ReadOnlyBufferException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_unwrap_03() {
+ public void test_unwrap_03() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbR = ByteBuffer.allocate(100).asReadOnlyBuffer();
@@ -459,10 +371,7 @@
try {
sse.unwrap(bb, bbA, 0, bbA.length);
fail("ReadOnlyBufferException wasn't thrown");
- } catch (ReadOnlyBufferException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of ReadOnlyBufferException");
+ } catch (ReadOnlyBufferException expected) {
}
}
@@ -471,8 +380,7 @@
* int offset, int length)
* Exception case: IllegalArgumentException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_unwrap_04() {
+ public void test_unwrap_04() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer[] bbA = {ByteBuffer.allocate(100), ByteBuffer.allocate(10), ByteBuffer.allocate(100)};
@@ -486,40 +394,23 @@
try {
sse.unwrap(bN, bbA, 0, 3);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.unwrap(bb, bbAN, 0, 3);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.unwrap(bb, bbN, 0, 0);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.unwrap(bN, bbN, 0, 0);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
-
}
/**
@@ -527,8 +418,7 @@
* int offset, int length)
* Exception case: IllegalStateException should be thrown.
*/
- @AndroidOnly("The RI doesn't throw the IllegalStateException.")
- public void test_unwrap_05() {
+ public void test_unwrap_05() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer[] bbA = { ByteBuffer.allocate(100), ByteBuffer.allocate(10), ByteBuffer.allocate(100) };
@@ -537,12 +427,9 @@
SSLEngine sse = getEngine(host, port);
try {
- sse.unwrap(bb, bbA, 0, bbA.length);
+ SSLEngineResult result = sse.unwrap(bb, bbA, 0, bbA.length);
fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalStateException");
+ } catch (IllegalStateException expected) {
}
}
@@ -550,7 +437,7 @@
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer[] dsts,
* int offset, int length)
*/
- public void test_unwrap_06() {
+ public void test_unwrap_06() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer[] bbA = { ByteBuffer.allocate(100), ByteBuffer.allocate(10), ByteBuffer.allocate(100) };
@@ -559,20 +446,16 @@
SSLEngine sse = getEngine(host, port);
sse.setUseClientMode(true);
- try {
- SSLEngineResult res = sse.unwrap(bb, bbA, 0, bbA.length);
- assertEquals(0, res.bytesConsumed());
- assertEquals(0, res.bytesProduced());
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
+ SSLEngineResult res = sse.unwrap(bb, bbA, 0, bbA.length);
+ assertEquals(0, res.bytesConsumed());
+ assertEquals(0, res.bytesProduced());
}
- public void test_wrap_01() throws IOException, InterruptedException {
+ public void test_wrap_01() throws Exception {
prepareEngines();
doHandshake();
ByteBuffer bbs = ByteBuffer.allocate(100);
- ByteBuffer bbd = ByteBuffer.allocate(20000);
+ ByteBuffer bbd = ByteBuffer.allocate(MAX_TLS_RECORD_SIZE);
clientEngine.engine.wrap(new ByteBuffer[] { bbs }, 0, 1, bbd);
}
@@ -581,8 +464,7 @@
* int length, ByteBuffer dst)
* Exception case: IndexOutOfBoundsException should be thrown.
*/
- @KnownFailure("Fixed in DonutBurger, boundary checks missing")
- public void test_wrap_02() throws SSLException {
+ public void test_wrap_02() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bb = ByteBuffer.allocate(10);
@@ -593,26 +475,22 @@
try {
sse.wrap(bbA, -1, 3, bb);
fail("IndexOutOfBoundsException wasn't thrown");
- } catch (IndexOutOfBoundsException iobe) {
- //expected
+ } catch (IndexOutOfBoundsException expected) {
}
try {
sse.wrap(bbA, 0, -3, bb);
fail("IndexOutOfBoundsException wasn't thrown");
- } catch (IndexOutOfBoundsException iobe) {
- //expected
+ } catch (IndexOutOfBoundsException expected) {
}
try {
sse.wrap(bbA, bbA.length + 1, bbA.length, bb);
fail("IndexOutOfBoundsException wasn't thrown");
- } catch (IndexOutOfBoundsException iobe) {
- //expected
+ } catch (IndexOutOfBoundsException expected) {
}
try {
sse.wrap(bbA, 0, bbA.length + 1, bb);
fail("IndexOutOfBoundsException wasn't thrown");
- } catch (IndexOutOfBoundsException iobe) {
- //expected
+ } catch (IndexOutOfBoundsException expected) {
}
}
@@ -621,10 +499,10 @@
* int length, ByteBuffer dst)
* Exception case: ReadOnlyBufferException should be thrown.
*/
- public void test_wrap_03() throws SSLException {
+ public void test_wrap_03() throws Exception {
String host = "new host";
int port = 8080;
- ByteBuffer bb = ByteBuffer.allocate(10).asReadOnlyBuffer();
+ ByteBuffer bb = ByteBuffer.allocate(MAX_TLS_RECORD_SIZE).asReadOnlyBuffer();
ByteBuffer[] bbA = {ByteBuffer.allocate(5), ByteBuffer.allocate(10), ByteBuffer.allocate(5)};
SSLEngine sse = getEngine(host, port);
sse.setUseClientMode(true);
@@ -632,8 +510,7 @@
try {
sse.wrap(bbA, 0, bbA.length, bb);
fail("ReadOnlyBufferException wasn't thrown");
- } catch (ReadOnlyBufferException iobe) {
- //expected
+ } catch (ReadOnlyBufferException expected) {
}
}
@@ -642,8 +519,7 @@
* int length, ByteBuffer dst)
* Exception case: IllegalArgumentException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_wrap_04() {
+ public void test_wrap_04() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer[] bbA = {ByteBuffer.allocate(100), ByteBuffer.allocate(10), ByteBuffer.allocate(100)};
@@ -655,19 +531,13 @@
try {
e.wrap(bbA, 0, 3, bN);
fail("IllegalArgumentException must be thrown for null srcs byte buffer array");
- } catch (NullPointerException npe) {
} catch (IllegalArgumentException ex) {
- } catch (Exception ex) {
- fail(ex + " was thrown instead of IllegalArgumentException");
}
try {
e.wrap(bbN, 0, 0, bN);
fail("IllegalArgumentException wasn't thrown");
} catch (IllegalArgumentException ex) {
- } catch (NullPointerException npe) {
- } catch (Exception ex) {
- fail(ex + " was thrown instead of IllegalArgumentException");
}
}
@@ -676,19 +546,17 @@
* int length, ByteBuffer dst)
* Exception case: IllegalStateException should be thrown.
*/
- @AndroidOnly("The RI doesn't throw the IllegalStateException.")
- public void test_wrap_05() throws SSLException {
+ public void test_wrap_05() throws Exception {
String host = "new host";
int port = 8080;
- ByteBuffer bb = ByteBuffer.allocate(10);
+ ByteBuffer bb = ByteBuffer.allocate(MAX_TLS_RECORD_SIZE);
ByteBuffer[] bbA = {ByteBuffer.allocate(5), ByteBuffer.allocate(10), ByteBuffer.allocate(5)};
SSLEngine sse = getEngine(host, port);
try {
- sse.wrap(bbA, 0, bbA.length, bb);
- fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException iobe) {
- //expected
+ SSLEngineResult result = sse.wrap(bbA, 0, bbA.length, bb);
+ fail("Should fail since mode not set yet");
+ } catch (IllegalStateException expected) {
}
}
@@ -696,60 +564,49 @@
* javax.net.ssl.SSLEngine#wrap(ByteBuffer[] srcs, int offset,
* int length, ByteBuffer dst)
*/
- public void test_wrap_06() {
+ public void test_wrap_06() throws Exception {
String host = "new host";
int port = 8080;
- ByteBuffer bb = ByteBuffer.allocate(10);
+ ByteBuffer bb = ByteBuffer.allocate(MAX_TLS_RECORD_SIZE);
ByteBuffer[] bbA = {ByteBuffer.allocate(5), ByteBuffer.allocate(10), ByteBuffer.allocate(5)};
SSLEngine sse = getEngine(host, port);
sse.setUseClientMode(true);
- try {
- sse.wrap(bbA, 0, bbA.length, bb);
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
+ SSLEngineResult result = sse.wrap(bbA, 0, bbA.length, bb);
+ assertEquals(SSLEngineResult.Status.OK, result.getStatus());
+ assertEquals(0, result.bytesConsumed());
+ assertTrue(result.bytesProduced() > 0);
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#closeOutbound()
* javax.net.ssl.SSLEngine#isOutboundDone()
*/
- public void test_closeOutbound() throws NoSuchAlgorithmException {
+ public void test_closeOutbound() throws Exception {
SSLEngine sse = getEngine();
- try {
- assertFalse(sse.isOutboundDone());
- sse.closeOutbound();
- assertTrue(sse.isOutboundDone());
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
+ assertFalse(sse.isOutboundDone());
+ sse.closeOutbound();
+ assertTrue(sse.isOutboundDone());
}
/**
- * @throws NoSuchAlgorithmException
* javax.net.ssl.SSLEngine#closeInbound()
* javax.net.ssl.SSLEngine#isInboundDone()
*/
- public void test_closeInbound() throws NoSuchAlgorithmException {
+ public void test_closeInbound() throws Exception {
SSLEngine sse = getEngine();
- try {
- assertFalse(sse.isInboundDone());
- sse.closeInbound();
- assertTrue(sse.isInboundDone());
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
+ assertFalse(sse.isInboundDone());
+ sse.closeInbound();
+ assertTrue(sse.isInboundDone());
}
/**
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer dst)
* SSLException should be thrown.
*/
- public void test_unwrap_ByteBuffer_ByteBuffer_01() throws InterruptedException, IOException {
+ public void test_unwrap_ByteBuffer_ByteBuffer_01() throws Exception {
prepareEngines();
doHandshake();
ByteBuffer bbs = ByteBuffer.allocate(100);
@@ -767,8 +624,7 @@
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer dst)
* ReadOnlyBufferException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_unwrap_ByteBuffer_ByteBuffer_02() {
+ public void test_unwrap_ByteBuffer_ByteBuffer_02() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbs = ByteBuffer.allocate(10);
@@ -790,8 +646,7 @@
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer dst)
* IllegalArgumentException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_unwrap_ByteBuffer_ByteBuffer_03() {
+ public void test_unwrap_ByteBuffer_ByteBuffer_03() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbsN = null;
@@ -804,31 +659,19 @@
try {
sse.unwrap(bbsN, bbd);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.unwrap(bbs, bbdN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.unwrap(bbsN, bbdN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
@@ -836,8 +679,7 @@
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer dst)
* IllegalStateException should be thrown.
*/
- @AndroidOnly("The RI doesn't throw the IllegalStateException.")
- public void test_unwrap_ByteBuffer_ByteBuffer_04() {
+ public void test_unwrap_ByteBuffer_ByteBuffer_04() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbs = ByteBuffer.allocate(10);
@@ -847,17 +689,14 @@
try {
sse.unwrap(bbs, bbd);
fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalStateException");
+ } catch (IllegalStateException expected) {
}
}
/**
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer dst)
*/
- public void test_unwrap_ByteBuffer_ByteBuffer_05() {
+ public void test_unwrap_ByteBuffer_ByteBuffer_05() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbs = ByteBuffer.allocate(10);
@@ -865,20 +704,16 @@
SSLEngine sse = getEngine(host, port);
sse.setUseClientMode(true);
- try {
- SSLEngineResult res = sse.unwrap(bbs, bbd);
- assertEquals(0, res.bytesConsumed());
- assertEquals(0, res.bytesProduced());
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
+ SSLEngineResult res = sse.unwrap(bbs, bbd);
+ assertEquals(0, res.bytesConsumed());
+ assertEquals(0, res.bytesProduced());
}
/**
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer[] dsts)
* SSLException should be thrown.
*/
- public void test_unwrap_ByteBuffer$ByteBuffer_01() throws IOException, InterruptedException {
+ public void test_unwrap_ByteBuffer$ByteBuffer_01() throws Exception {
prepareEngines();
doHandshake();
@@ -888,8 +723,7 @@
try {
clientEngine.engine.unwrap(bbs, new ByteBuffer[] { bbd });
fail("SSLException wasn't thrown");
- } catch (SSLException ex) {
- //expected
+ } catch (SSLException expected) {
}
}
@@ -897,8 +731,7 @@
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer[] dsts)
* ReadOnlyBufferException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_unwrap_ByteBuffer$ByteBuffer_02() {
+ public void test_unwrap_ByteBuffer$ByteBuffer_02() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbs = ByteBuffer.allocate(10);
@@ -910,10 +743,7 @@
try {
sse.unwrap(bbs, bbA);
fail("ReadOnlyBufferException wasn't thrown");
- } catch (ReadOnlyBufferException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of ReadOnlyBufferException");
+ } catch (ReadOnlyBufferException expected) {
}
}
@@ -921,8 +751,7 @@
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer[] dsts)
* IllegalArgumentException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_unwrap_ByteBuffer$ByteBuffer_03() {
+ public void test_unwrap_ByteBuffer$ByteBuffer_03() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer[] bbA = { ByteBuffer.allocate(100), ByteBuffer.allocate(10), ByteBuffer.allocate(100) };
@@ -936,41 +765,25 @@
try {
sse.unwrap(bN, bbA);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.unwrap(bb, bbAN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.unwrap(bb, bbN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.unwrap(bN, bbAN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
@@ -978,8 +791,7 @@
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer[] dsts)
* IllegalStateException should be thrown.
*/
- @AndroidOnly("The RI doesn't throw the IllegalStateException.")
- public void test_unwrap_ByteBuffer$ByteBuffer_04() {
+ public void test_unwrap_ByteBuffer$ByteBuffer_04() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbs = ByteBuffer.allocate(10);
@@ -989,17 +801,14 @@
try {
sse.unwrap(bbs, bbd);
fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalStateException");
+ } catch (IllegalStateException expected) {
}
}
/**
* javax.net.ssl.SSLEngine#unwrap(ByteBuffer src, ByteBuffer[] dsts)
*/
- public void test_unwrap_ByteBuffer$ByteBuffer_05() {
+ public void test_unwrap_ByteBuffer$ByteBuffer_05() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbs = ByteBuffer.allocate(10);
@@ -1007,16 +816,12 @@
SSLEngine sse = getEngine(host, port);
sse.setUseClientMode(true);
- try {
- SSLEngineResult res = sse.unwrap(bbs, bbd);
- assertEquals(0, res.bytesConsumed());
- assertEquals(0, res.bytesProduced());
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
+ SSLEngineResult res = sse.unwrap(bbs, bbd);
+ assertEquals(0, res.bytesConsumed());
+ assertEquals(0, res.bytesProduced());
}
- public void test_wrap_ByteBuffer_ByteBuffer_01() throws IOException, InterruptedException {
+ public void test_wrap_ByteBuffer_ByteBuffer_01() throws Exception {
prepareEngines();
doHandshake();
ByteBuffer bbs = ByteBuffer.allocate(20);
@@ -1028,7 +833,7 @@
* javax.net.ssl.SSLEngine#wrap(ByteBuffer src, ByteBuffer dst)
* ReadOnlyBufferException should be thrown.
*/
- public void test_wrap_ByteBuffer_ByteBuffer_02() {
+ public void test_wrap_ByteBuffer_ByteBuffer_02() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbs = ByteBuffer.allocate(10);
@@ -1039,10 +844,7 @@
try {
sse.wrap(bbs, bbd);
fail("ReadOnlyBufferException wasn't thrown");
- } catch (ReadOnlyBufferException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of ReadOnlyBufferException");
+ } catch (ReadOnlyBufferException expected) {
}
}
@@ -1050,8 +852,7 @@
* javax.net.ssl.SSLEngine#wrap(ByteBuffer src, ByteBuffer dst)
* IllegalArgumentException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_wrap_ByteBuffer_ByteBuffer_03() {
+ public void test_wrap_ByteBuffer_ByteBuffer_03() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbsN = null;
@@ -1064,31 +865,19 @@
try {
sse.wrap(bbsN, bbd);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.wrap(bbs, bbdN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.wrap(bbsN, bbdN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
@@ -1096,8 +885,7 @@
* javax.net.ssl.SSLEngine#wrap(ByteBuffer src, ByteBuffer dst)
* IllegalStateException should be thrown.
*/
- @AndroidOnly("The RI doesn't throw the IllegalStateException.")
- public void test_wrap_ByteBuffer_ByteBuffer_04() {
+ public void test_wrap_ByteBuffer_ByteBuffer_04() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bbs = ByteBuffer.allocate(10);
@@ -1105,60 +893,46 @@
SSLEngine sse = getEngine(host, port);
try {
- sse.wrap(bbs, bbd);
- fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalStateException");
+ SSLEngineResult result = sse.wrap(bbs, bbd);
+ } catch (IllegalStateException expected) {
}
}
/**
* javax.net.ssl.SSLEngine#wrap(ByteBuffer src, ByteBuffer dst)
*/
- public void test_wrap_ByteBuffer_ByteBuffer_05() {
+ public void test_wrap_ByteBuffer_ByteBuffer_05() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bb = ByteBuffer.allocate(10);
SSLEngine sse = getEngine(host, port);
sse.setUseClientMode(true);
- try {
- SSLEngineResult res = sse.wrap(bb, ByteBuffer.allocate(10));
- assertEquals(0, res.bytesConsumed());
- assertEquals(0, res.bytesProduced());
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
+ SSLEngineResult res = sse.wrap(bb, ByteBuffer.allocate(10));
+ assertEquals(Status.BUFFER_OVERFLOW, res.getStatus());
+ assertEquals(0, res.bytesConsumed());
+ assertEquals(0, res.bytesProduced());
}
/**
- * @throws IOException
- * @throws InterruptedException
* javax.net.ssl.SSLEngine#wrap(ByteBuffer[] srcs, ByteBuffer dst)
* SSLException should be thrown.
*/
- public void test_wrap_ByteBuffer$ByteBuffer_01() throws IOException, InterruptedException {
+ public void test_wrap_ByteBuffer$ByteBuffer_01() throws Exception {
prepareEngines();
doHandshake();
ByteBuffer bbs = ByteBuffer.allocate(100);
ByteBuffer bbd = ByteBuffer.allocate(20000);
- try {
- clientEngine.engine.wrap(new ByteBuffer[] { bbs }, bbd);
- serverEngine.engine.wrap(new ByteBuffer[] { bbs }, bbd);
- //fail("SSLException wasn't thrown");
- } catch (SSLException ex) {
- //expected
- }
+ clientEngine.engine.wrap(new ByteBuffer[] { bbs }, bbd);
+ serverEngine.engine.wrap(new ByteBuffer[] { bbs }, bbd);
}
/**
* javax.net.ssl.SSLEngine#wrap(ByteBuffer[] srcs, ByteBuffer dst)
* ReadOnlyBufferException should be thrown.
*/
- public void test_wrap_ByteBuffer$ByteBuffer_02() {
+ public void test_wrap_ByteBuffer$ByteBuffer_02() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bb = ByteBuffer.allocate(10).asReadOnlyBuffer();
@@ -1169,10 +943,7 @@
try {
sse.wrap(bbA, bb);
fail("ReadOnlyBufferException wasn't thrown");
- } catch (ReadOnlyBufferException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of ReadOnlyBufferException");
+ } catch (ReadOnlyBufferException expected) {
}
}
@@ -1180,8 +951,7 @@
* javax.net.ssl.SSLEngine#wrap(ByteBuffer[] srcs, ByteBuffer dst)
* IllegalArgumentException should be thrown.
*/
- @KnownFailure("Fixed on DonutBurger, Wrong Exception thrown")
- public void test_wrap_ByteBuffer$ByteBuffer_03() {
+ public void test_wrap_ByteBuffer$ByteBuffer_03() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer[] bbA = {ByteBuffer.allocate(100), ByteBuffer.allocate(10), ByteBuffer.allocate(100)};
@@ -1194,31 +964,19 @@
try {
sse.wrap(bbA, bN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.wrap(bbAN, bb);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
try {
sse.wrap(bbAN, bN);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iobe) {
- //expected
- } catch (NullPointerException npe) {
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
@@ -1226,69 +984,42 @@
* javax.net.ssl.SSLEngine#wrap(ByteBuffer[] srcs, ByteBuffer dst)
* IllegalStateException should be thrown.
*/
- @AndroidOnly("The RI doesn't throw the IllegalStateException.")
- public void test_wrap_ByteBuffer$ByteBuffer_04() {
+ public void test_wrap_ByteBuffer$ByteBuffer_04() throws Exception {
String host = "new host";
int port = 8080;
ByteBuffer bb = ByteBuffer.allocate(10);
ByteBuffer[] bbA = { ByteBuffer.allocate(5), ByteBuffer.allocate(10), ByteBuffer.allocate(5) };
SSLEngine sse = getEngine(host, port);
- try {
- sse.wrap(bbA, bb);
- fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException iobe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalStateException");
- }
+ SSLEngineResult result = sse.wrap(bbA, bb);
+ assertEquals(Status.BUFFER_OVERFLOW, result.getStatus());
}
/**
* javax.net.ssl.SSLEngine#wrap(ByteBuffer[] srcs, ByteBuffer dst)
*/
- public void test_wrap_ByteBuffer$ByteBuffer_05() {
+ public void test_wrap_ByteBuffer$ByteBuffer_05() throws Exception {
String host = "new host";
int port = 8080;
- ByteBuffer bb = ByteBuffer.allocate(10);
+ ByteBuffer bb = ByteBuffer.allocate(2000);
ByteBuffer[] bbA = { ByteBuffer.allocate(5), ByteBuffer.allocate(10), ByteBuffer.allocate(5) };
SSLEngine sse = getEngine(host, port);
sse.setUseClientMode(true);
- try {
- SSLEngineResult res = sse.wrap(bbA, bb);
- assertEquals(0, res.bytesConsumed());
- assertEquals(0, res.bytesProduced());
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
+ SSLEngineResult res = sse.wrap(bbA, bb);
+ assertEquals(0, res.bytesConsumed());
+ assertEquals(0, res.bytesProduced());
}
- private SSLEngine getEngine() {
- SSLContext context = null;
- try {
- context = SSLContext.getInstance("TLS");
- context.init(null, null, null);
- } catch (KeyManagementException e) {
- fail("Could not get SSLEngine: key management exception "
- + e.getMessage());
- } catch (NoSuchAlgorithmException e) {
- fail("Could not get SSLEngine: no such algorithm " + e.getMessage());
- }
+ private SSLEngine getEngine() throws Exception {
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, null, null);
return context.createSSLEngine();
}
- private SSLEngine getEngine(String host, int port) {
- SSLContext context = null;
- try {
- context = SSLContext.getInstance("TLS");
- context.init(null, null, null);
- } catch (KeyManagementException e) {
- fail("Could not get SSLEngine: key management exception "
- + e.getMessage());
- } catch (NoSuchAlgorithmException e) {
- fail("Could not get SSLEngine: no such algorithm " + e.getMessage());
- }
+ private SSLEngine getEngine(String host, int port) throws Exception {
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, null, null);
return context.createSSLEngine(host, port);
}
@@ -1311,8 +1042,7 @@
private ByteBuffer writeBuffer;
- HandshakeHandler(boolean clientMode, SourceChannel in, SinkChannel out)
- throws SSLException {
+ HandshakeHandler(boolean clientMode, SourceChannel in, SinkChannel out) throws Exception {
this.in = in;
this.out = out;
engine = getEngine();
@@ -1424,9 +1154,7 @@
}
}
- @KnownFailure("Handshake Status is never finished. NPE in "
- + "ClientSessionContext$HostAndPort.hashCode() when host is null")
- public void testHandshake() throws IOException, InterruptedException {
+ public void testHandshake() throws Exception {
prepareEngines();
@@ -1442,7 +1170,7 @@
serverEngine.getStatus());
}
- void prepareEngines() throws IOException {
+ void prepareEngines() throws Exception {
Pipe clientSendPipe = Pipe.open();
Pipe serverSendPipe = Pipe.open();
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 39cd326..042bcd1 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -223,6 +223,13 @@
public native Object newNonMovableArray(Class<?> componentType, int length);
/**
+ * Returns an array of at least minLength, but potentially larger. The increased size comes from
+ * avoiding any padding after the array. The amount of padding varies depending on the
+ * componentType and the memory allocator implementation.
+ */
+ public native Object newUnpaddedArray(Class<?> componentType, int minLength);
+
+ /**
* Returns the address of array[0]. This differs from using JNI in that JNI might lie and
* give you the address of a copy of the array when in forcecopy mode.
*/
@@ -272,5 +279,5 @@
/**
* Register application info
*/
- public static native void registerAppInfo(String appDir, String processName);
+ public static native void registerAppInfo(String appDir, String processName, String pkgname);
}
diff --git a/libart/src/main/java/java/lang/Class.java b/libart/src/main/java/java/lang/Class.java
index 69e4e43..c66b01a 100644
--- a/libart/src/main/java/java/lang/Class.java
+++ b/libart/src/main/java/java/lang/Class.java
@@ -62,6 +62,7 @@
import libcore.util.BasicLruCache;
import libcore.util.CollectionUtils;
import libcore.util.EmptyArray;
+import libcore.util.SneakyThrow;
/**
* The in-memory representation of a Java class. This representation serves as
@@ -1553,7 +1554,7 @@
}
Class<?> caller = VMStack.getStackClass1();
if (!caller.canAccess(this)) {
- throw new IllegalAccessException(this + " is not accessible from " + caller);
+ throw new IllegalAccessException(this + " is not accessible from " + caller);
}
Constructor<T> init;
try {
@@ -1565,14 +1566,13 @@
throw t;
}
if (!caller.canAccessMember(this, init.getAccessFlags())) {
- throw new IllegalAccessException(init + " is not accessible from " + caller);
+ throw new IllegalAccessException(init + " is not accessible from " + caller);
}
try {
- return init.newInstance();
+ return init.newInstance(null, init.isAccessible());
} catch (InvocationTargetException e) {
- InstantiationException t = new InstantiationException(this);
- t.initCause(e);
- throw t;
+ SneakyThrow.sneakyThrow(e.getCause());
+ return null; // Unreachable.
}
}
diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java
index f71acb1..e4caffa 100644
--- a/libart/src/main/java/java/lang/DexCache.java
+++ b/libart/src/main/java/java/lang/DexCache.java
@@ -71,7 +71,7 @@
String[] strings;
/** Holds C pointer to dexFile. */
- private int dexFile;
+ private long dexFile;
// Only created by the VM.
private DexCache() {}
diff --git a/libart/src/main/java/java/lang/Enum.java b/libart/src/main/java/java/lang/Enum.java
index 43ac113..e9545a1 100644
--- a/libart/src/main/java/java/lang/Enum.java
+++ b/libart/src/main/java/java/lang/Enum.java
@@ -39,6 +39,7 @@
}
try {
Method method = enumType.getDeclaredMethod("values", EmptyArray.CLASS);
+ method.setAccessible(true);
return (Object[]) method.invoke((Object[]) null);
} catch (NoSuchMethodException impossible) {
throw new AssertionError("impossible", impossible);
diff --git a/libart/src/main/java/java/lang/Object.java b/libart/src/main/java/java/lang/Object.java
index 9c59870..62a2a59 100644
--- a/libart/src/main/java/java/lang/Object.java
+++ b/libart/src/main/java/java/lang/Object.java
@@ -133,6 +133,15 @@
private transient Class<?> shadow$_klass_;
private transient int shadow$_monitor_;
+ // Uncomment the following two fields to enable brooks pointers.
+ // Meant to do "#ifdef USE_BROOKS_POINTER ... #endif" but no macros.
+ //
+ // Note names use a 'x' prefix and the _x_rb_ptr_ field is of
+ // type int instead of Object to go with the alphabetical/by-type
+ // field order.
+ // private transient int shadow$_x_rb_ptr_;
+ // private transient int shadow$_x_xpadding_;
+
/**
* Constructs a new instance of {@code Object}.
*/
@@ -278,10 +287,9 @@
* that called {@code notify()} has to release the object's monitor first.
* Also, the chosen thread still has to compete against other threads that
* try to synchronize on the same object.
- * <p>
- * This method can only be invoked by a thread which owns this object's
+ *
+ * <p>This method can only be invoked by a thread which owns this object's
* monitor. A thread becomes owner of an object's monitor
- * </p>
* <ul>
* <li>by executing a synchronized method of that object;</li>
* <li>by executing the body of a {@code synchronized} statement that
@@ -304,10 +312,9 @@
* will not run immediately. The thread that called {@code notify()} has to
* release the object's monitor first. Also, the threads still have to
* compete against other threads that try to synchronize on the same object.
- * <p>
- * This method can only be invoked by a thread which owns this object's
+ *
+ * <p>This method can only be invoked by a thread which owns this object's
* monitor. A thread becomes owner of an object's monitor
- * </p>
* <ul>
* <li>by executing a synchronized method of that object;</li>
* <li>by executing the body of a {@code synchronized} statement that
@@ -349,23 +356,22 @@
* notify()} or {@code notifyAll()} method of this object. This method can
* only be invoked by a thread which owns this object's monitor; see
* {@link #notify()} on how a thread can become the owner of a monitor.
- * <p>
- * A waiting thread can be sent {@code interrupt()} to cause it to
+ *
+ * <p>A waiting thread can be sent {@code interrupt()} to cause it to
* prematurely stop waiting, so {@code wait} should be called in a loop to
* check that the condition that has been waited for has been met before
* continuing.
- * </p>
- * <p>
- * While the thread waits, it gives up ownership of this object's monitor.
- * When it is notified (or interrupted), it re-acquires the monitor before
- * it starts running.
- * </p>
+ *
+ * <p>While the thread waits, it gives up ownership of this object's
+ * monitor. When it is notified (or interrupted), it re-acquires the monitor
+ * before it starts running.
*
* @throws IllegalMonitorStateException
* if the thread calling this method is not the owner of this
* object's monitor.
- * @throws InterruptedException
- * if another thread interrupts this thread while it is waiting.
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see #notify
* @see #notifyAll
* @see #wait(long)
@@ -380,17 +386,18 @@
* specified timeout expires. This method can only be invoked by a thread
* which owns this object's monitor; see {@link #notify()} on how a thread
* can become the owner of a monitor.
- * <p>
- * A waiting thread can be sent {@code interrupt()} to cause it to
+ *
+ * <p>A waiting thread can be sent {@code interrupt()} to cause it to
* prematurely stop waiting, so {@code wait} should be called in a loop to
* check that the condition that has been waited for has been met before
* continuing.
- * </p>
- * <p>
- * While the thread waits, it gives up ownership of this object's monitor.
- * When it is notified (or interrupted), it re-acquires the monitor before
- * it starts running.
- * </p>
+ *
+ * <p>While the thread waits, it gives up ownership of this object's
+ * monitor. When it is notified (or interrupted), it re-acquires the monitor
+ * before it starts running.
+ *
+ * <p>A timeout of zero means the calling thread should wait forever unless interrupted or
+ * notified.
*
* @param millis
* the maximum time to wait in milliseconds.
@@ -399,8 +406,9 @@
* @throws IllegalMonitorStateException
* if the thread calling this method is not the owner of this
* object's monitor.
- * @throws InterruptedException
- * if another thread interrupts this thread while it is waiting.
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see #notify
* @see #notifyAll
* @see #wait()
@@ -417,17 +425,18 @@
* specified timeout expires. This method can only be invoked by a thread
* that owns this object's monitor; see {@link #notify()} on how a thread
* can become the owner of a monitor.
- * <p>
- * A waiting thread can be sent {@code interrupt()} to cause it to
+ *
+ * <p>A waiting thread can be sent {@code interrupt()} to cause it to
* prematurely stop waiting, so {@code wait} should be called in a loop to
* check that the condition that has been waited for has been met before
* continuing.
- * </p>
- * <p>
- * While the thread waits, it gives up ownership of this object's monitor.
- * When it is notified (or interrupted), it re-acquires the monitor before
- * it starts running.
- * </p>
+ *
+ * <p>While the thread waits, it gives up ownership of this object's
+ * monitor. When it is notified (or interrupted), it re-acquires the monitor
+ * before it starts running.
+ *
+ * <p>A timeout of zero means the calling thread should wait forever unless interrupted or
+ * notified.
*
* @param millis
* the maximum time to wait in milliseconds.
@@ -440,8 +449,9 @@
* @throws IllegalMonitorStateException
* if the thread calling this method is not the owner of this
* object's monitor.
- * @throws InterruptedException
- * if another thread interrupts this thread while it is waiting.
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see #notify
* @see #notifyAll
* @see #wait()
diff --git a/libart/src/main/java/java/lang/Thread.java b/libart/src/main/java/java/lang/Thread.java
index 5c81e36..852e2cf 100644
--- a/libart/src/main/java/java/lang/Thread.java
+++ b/libart/src/main/java/java/lang/Thread.java
@@ -124,8 +124,8 @@
*/
public static final int NORM_PRIORITY = 5;
- /* some of these are accessed directly by the VM; do not rename them */
- private volatile int nativePeer;
+ /* Some of these are accessed directly by the VM; do not rename them. */
+ private volatile long nativePeer;
volatile ThreadGroup group;
volatile boolean daemon;
volatile String name;
@@ -712,8 +712,9 @@
* Blocks the current Thread (<code>Thread.currentThread()</code>) until
* the receiver finishes its execution and dies.
*
- * @throws InterruptedException if <code>interrupt()</code> was called for
- * the receiver while it was in the <code>join()</code> call
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception is
+ * thrown.
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
@@ -730,9 +731,12 @@
* the receiver finishes its execution and dies or the specified timeout
* expires, whatever happens first.
*
+ * <p>A timeout of zero means the calling thread should wait forever unless interrupted.
+ *
* @param millis The maximum time to wait (in milliseconds).
- * @throws InterruptedException if <code>interrupt()</code> was called for
- * the receiver while it was in the <code>join()</code> call
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception is
+ * thrown.
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
@@ -745,10 +749,13 @@
* the receiver finishes its execution and dies or the specified timeout
* expires, whatever happens first.
*
+ * <p>A timeout of zero means the calling thread should wait forever unless interrupted.
+ *
* @param millis The maximum time to wait (in milliseconds).
* @param nanos Extra nanosecond precision
- * @throws InterruptedException if <code>interrupt()</code> was called for
- * the receiver while it was in the <code>join()</code> call
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception is
+ * thrown.
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
@@ -969,9 +976,9 @@
*
* @param time
* The time to sleep in milliseconds.
- * @throws InterruptedException
- * if <code>interrupt()</code> was called for this Thread while
- * it was sleeping
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see Thread#interrupt()
*/
public static void sleep(long time) throws InterruptedException {
@@ -987,15 +994,29 @@
* The time to sleep in milliseconds.
* @param nanos
* Extra nanosecond precision
- * @throws InterruptedException
- * if <code>interrupt()</code> was called for this Thread while
- * it was sleeping
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see Thread#interrupt()
*/
public static void sleep(long millis, int nanos) throws InterruptedException {
+ if (millis < 0) {
+ throw new IllegalArgumentException("millis < 0: " + millis);
+ }
+ if (nanos < 0) {
+ throw new IllegalArgumentException("nanos < 0: " + nanos);
+ }
+ if (nanos > 999999) {
+ throw new IllegalArgumentException("nanos > 999999: " + nanos);
+ }
+
// The JLS 3rd edition, section 17.9 says: "...sleep for zero
// time...need not have observable effects."
if (millis == 0 && nanos == 0) {
+ // ...but we still have to handle being interrupted.
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
return;
}
diff --git a/libart/src/main/java/java/lang/reflect/ArtMethod.java b/libart/src/main/java/java/lang/reflect/ArtMethod.java
index 9e90c8f..6ef06ee 100644
--- a/libart/src/main/java/java/lang/reflect/ArtMethod.java
+++ b/libart/src/main/java/java/lang/reflect/ArtMethod.java
@@ -42,38 +42,83 @@
* @hide
*/
public final class ArtMethod {
+ /* A note on the field order here, it reflects the same field order as laid out by ART. */
/** Method's declaring class */
private Class<?> declaringClass;
- /** Method access flags (modifiers) */
- private int accessFlags;
- /** DexFile index */
- private int methodDexIndex;
- /** Dispatch table entry */
- private int methodIndex;
- /** DexFile offset of CodeItem for this Method */
- private int codeItemOffset;
- /* ART compiler meta-data */
- private int frameSizeInBytes;
- private int coreSpillMask;
- private int fpSpillMask;
- private int mappingTable;
- private int gcMap;
- private int vmapTable;
- /** ART: compiled managed code associated with this Method */
- private int entryPointFromCompiledCode;
- /** ART: entry point from interpreter associated with this Method */
- private int entryPointFromInterpreter;
- /** ART: if this is a native method, the native code that will be invoked */
- private int nativeMethod;
- /* ART: dex cache fast access */
- private String[] dexCacheStrings;
- Class<?>[] dexCacheResolvedTypes;
+
+ /** Short-cut to declaringClass.dexCache.resolvedMethods */
private ArtMethod[] dexCacheResolvedMethods;
+ /** Short-cut to declaringClass.dexCache.resolvedTypes */
+ /* package */ Class<?>[] dexCacheResolvedTypes;
+
+ /** Short-cut to declaringClass.dexCache.strings */
+ private String[] dexCacheStrings;
+
/**
- * Only created by art directly.
+ * Method dispatch from the interpreter invokes this pointer which may cause a bridge into
+ * compiled code.
*/
+ private long entryPointFromInterpreter;
+
+ /**
+ * Pointer to JNI function registered to this method, or a function to resolve the JNI function.
+ */
+ private long entryPointFromJni;
+
+ /**
+ * Method dispatch from portable compiled code invokes this pointer which may cause bridging
+ * into quick compiled code or the interpreter.
+ */
+ private long entryPointFromPortableCompiledCode;
+
+ /**
+ * Method dispatch from quick compiled code invokes this pointer which may cause bridging
+ * into portable compiled code or the interpreter.
+ */
+ private long entryPointFromQuickCompiledCode;
+
+ /**
+ * Pointer to a data structure created by the compiler and used by the garbage collector to
+ * determine which registers hold live references to objects within the heap.
+ */
+ private long gcMap;
+
+ /** Bits encoding access (e.g. public, private) as well as other runtime specific flags */
+ private int accessFlags;
+
+ /* Dex file fields. The defining dex file is available via declaringClass.dexCache */
+
+ /** The offset of the code item associated with this method within its defining dex file */
+ private int dexCodeItemOffset;
+
+ /** The method index of this method within its defining dex file */
+ private int dexMethodIndex;
+
+ /* End of dex file fields. */
+
+ /**
+ * Entry within a dispatch table for this method. For static/direct methods the index is
+ * into the declaringClass.directMethods, for virtual methods the vtable and for
+ * interface methods the ifTable.
+ */
+ private int methodIndex;
+
+ /* Quick compiler meta-data. TODO: merge and place in native heap. */
+
+ /** Bit map of spilled machine registers. */
+ private int quickCoreSpillMask;
+
+ /** Bit map of spilled floating point machine registers. */
+ private int quickFpSpillMask;
+
+ /** Fixed frame size for this method when executed. */
+ private int quickFrameSizeInBytes;
+
+ /* End of quick compiler meta-data. */
+
+ /** Only created by ART directly. */
private ArtMethod() {}
Class getDeclaringClass() {
@@ -85,7 +130,7 @@
}
int getDexMethodIndex() {
- return methodDexIndex;
+ return dexMethodIndex;
}
public static String getMethodName(ArtMethod artMethod) {
@@ -126,7 +171,7 @@
Class<?>[] getParameterTypes() {
Dex dex = getDeclaringClass().getDex();
- short[] types = dex.parameterTypeIndicesFromMethodIndex(methodDexIndex);
+ short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex);
if (types.length == 0) {
return EmptyArray.CLASS;
}
@@ -140,7 +185,7 @@
Class<?> getReturnType() {
Dex dex = declaringClass.getDex();
- int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(methodDexIndex);
+ int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(dexMethodIndex);
// Note, in the case of a Proxy the dex cache types are equal.
return getDexCacheType(dex, returnTypeIndex);
}
@@ -152,7 +197,7 @@
*/
int compareParameters(Class<?>[] params) {
Dex dex = getDeclaringClass().getDex();
- short[] types = dex.parameterTypeIndicesFromMethodIndex(methodDexIndex);
+ short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex);
int length = Math.min(types.length, params.length);
for (int i = 0; i < length; i++) {
Class<?> aType = getDexCacheType(dex, types[i]);
@@ -168,7 +213,7 @@
}
Annotation[][] getParameterAnnotations() {
- return AnnotationAccess.getParameterAnnotations(declaringClass, methodDexIndex);
+ return AnnotationAccess.getParameterAnnotations(declaringClass, dexMethodIndex);
}
/**
@@ -210,7 +255,7 @@
// Proxy method's declaring class' dex cache refers to that of Proxy. The local cache in
// Method refers to the original interface's dex cache and is ensured to be resolved by
// proxy generation.
- return dexCacheResolvedMethods[methodDexIndex];
+ return dexCacheResolvedMethods[dexMethodIndex];
}
return this;
}
diff --git a/libart/src/main/java/java/lang/reflect/Constructor.java b/libart/src/main/java/java/lang/reflect/Constructor.java
index b3df2f0..b1efe06 100644
--- a/libart/src/main/java/java/lang/reflect/Constructor.java
+++ b/libart/src/main/java/java/lang/reflect/Constructor.java
@@ -283,8 +283,14 @@
*
* @see AccessibleObject
*/
- public native T newInstance(Object... args) throws InstantiationException,
- IllegalAccessException, IllegalArgumentException, InvocationTargetException;
+ public T newInstance(Object... args) throws InstantiationException,
+ IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ return newInstance(args, isAccessible());
+ }
+
+ /** @hide */
+ public native T newInstance(Object[] args, boolean accessible) throws InstantiationException,
+ IllegalAccessException, IllegalArgumentException, InvocationTargetException;
/**
* Returns a string containing a concise, human-readable description of this
diff --git a/libart/src/main/java/java/lang/reflect/Field.java b/libart/src/main/java/java/lang/reflect/Field.java
index 4e982c7..11e8a6e 100644
--- a/libart/src/main/java/java/lang/reflect/Field.java
+++ b/libart/src/main/java/java/lang/reflect/Field.java
@@ -275,7 +275,12 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native Object get(Object object) throws IllegalAccessException, IllegalArgumentException;
+ public Object get(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return get(object, isAccessible());
+ }
+
+ private native Object get(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns the value of the field in the specified object as a {@code
@@ -300,8 +305,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native boolean getBoolean(Object object) throws IllegalAccessException,
- IllegalArgumentException;
+ public boolean getBoolean(Object object) throws IllegalAccessException,
+ IllegalArgumentException {
+ return getBoolean(object, isAccessible());
+ }
+
+ private native boolean getBoolean(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns the value of the field in the specified object as a {@code byte}.
@@ -326,8 +336,12 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native byte getByte(Object object) throws IllegalAccessException,
- IllegalArgumentException;
+ public byte getByte(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getByte(object, isAccessible());
+ }
+
+ private native byte getByte(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns the value of the field in the specified object as a {@code char}.
@@ -352,8 +366,12 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native char getChar(Object object) throws IllegalAccessException,
- IllegalArgumentException;
+ public char getChar(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getChar(object, isAccessible());
+ }
+
+ private native char getChar(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns the value of the field in the specified object as a {@code
@@ -378,8 +396,12 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native double getDouble(Object object) throws IllegalAccessException,
- IllegalArgumentException;
+ public double getDouble(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getDouble(object, isAccessible());
+ }
+
+ private native double getDouble(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns the value of the field in the specified object as a {@code float}
@@ -404,8 +426,12 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native float getFloat(Object object) throws IllegalAccessException,
- IllegalArgumentException;
+ public float getFloat(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getFloat(object, isAccessible());
+ }
+
+ private native float getFloat(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns the value of the field in the specified object as an {@code int}.
@@ -430,8 +456,12 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native int getInt(Object object) throws IllegalAccessException,
- IllegalArgumentException;
+ public int getInt(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getInt(object, isAccessible());
+ }
+
+ private native int getInt(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns the value of the field in the specified object as a {@code long}.
@@ -456,8 +486,12 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native long getLong(Object object) throws IllegalAccessException,
- IllegalArgumentException;
+ public long getLong(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getLong(object, isAccessible());
+ }
+
+ private native long getLong(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns the value of the field in the specified object as a {@code short}
@@ -482,8 +516,12 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native short getShort(Object object) throws IllegalAccessException,
- IllegalArgumentException;
+ public short getShort(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getShort(object, isAccessible());
+ }
+
+ private native short getShort(Object object, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Sets the value of the field in the specified object to the value. This
@@ -514,8 +552,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void set(Object object, Object value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void set(Object object, Object value) throws IllegalAccessException,
+ IllegalArgumentException {
+ set(object, value, isAccessible());
+ }
+
+ private native void set(Object object, Object value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Sets the value of the field in the specified object to the {@code
@@ -545,8 +588,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void setBoolean(Object object, boolean value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void setBoolean(Object object, boolean value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setBoolean(object, value, isAccessible());
+ }
+
+ private native void setBoolean(Object object, boolean value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Sets the value of the field in the specified object to the {@code byte}
@@ -575,8 +623,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void setByte(Object object, byte value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void setByte(Object object, byte value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setByte(object, value, isAccessible());
+ }
+
+ private native void setByte(Object object, byte value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Sets the value of the field in the specified object to the {@code char}
@@ -605,8 +658,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void setChar(Object object, char value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void setChar(Object object, char value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setChar(object, value, isAccessible());
+ }
+
+ private native void setChar(Object object, char value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Sets the value of the field in the specified object to the {@code double}
@@ -635,8 +693,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void setDouble(Object object, double value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void setDouble(Object object, double value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setDouble(object, value, isAccessible());
+ }
+
+ private native void setDouble(Object object, double value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Sets the value of the field in the specified object to the {@code float}
@@ -665,8 +728,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void setFloat(Object object, float value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void setFloat(Object object, float value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setFloat(object, value, isAccessible());
+ }
+
+ private native void setFloat(Object object, float value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Set the value of the field in the specified object to the {@code int}
@@ -695,8 +763,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void setInt(Object object, int value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void setInt(Object object, int value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setInt(object, value, isAccessible());
+ }
+
+ private native void setInt(Object object, int value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Sets the value of the field in the specified object to the {@code long}
@@ -725,8 +798,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void setLong(Object object, long value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void setLong(Object object, long value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setLong(object, value, isAccessible());
+ }
+
+ private native void setLong(Object object, long value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Sets the value of the field in the specified object to the {@code short}
@@ -755,8 +833,13 @@
* @throws IllegalAccessException
* if this field is not accessible
*/
- public native void setShort(Object object, short value) throws IllegalAccessException,
- IllegalArgumentException;
+ public void setShort(Object object, short value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setShort(object, value, isAccessible());
+ }
+
+ private native void setShort(Object object, short value, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException;
/**
* Returns a string containing a concise, human-readable description of this
diff --git a/libart/src/main/java/java/lang/reflect/Method.java b/libart/src/main/java/java/lang/reflect/Method.java
index 3089932..058fb96 100644
--- a/libart/src/main/java/java/lang/reflect/Method.java
+++ b/libart/src/main/java/java/lang/reflect/Method.java
@@ -367,8 +367,13 @@
* @throws InvocationTargetException
* if an exception was thrown by the invoked method
*/
- public native Object invoke(Object receiver, Object... args)
- throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
+ public Object invoke(Object receiver, Object... args)
+ throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ return invoke(receiver, args, isAccessible());
+ }
+
+ private native Object invoke(Object receiver, Object[] args, boolean accessible)
+ throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
/**
* Returns a string containing a concise, human-readable description of this
diff --git a/libart/src/main/java/sun/misc/Unsafe.java b/libart/src/main/java/sun/misc/Unsafe.java
index 0a4d8b2..6f5f5ee 100644
--- a/libart/src/main/java/sun/misc/Unsafe.java
+++ b/libart/src/main/java/sun/misc/Unsafe.java
@@ -81,12 +81,7 @@
if (component == null) {
throw new IllegalArgumentException("Valid for array classes only: " + clazz);
}
- // TODO: make the following not specific to the object model.
- int offset = 12;
- if (component == long.class || component == double.class) {
- offset += 4; // 4 bytes of padding.
- }
- return offset;
+ return getArrayBaseOffsetForComponentType(component);
}
/**
@@ -100,21 +95,12 @@
if (component == null) {
throw new IllegalArgumentException("Valid for array classes only: " + clazz);
}
- // TODO: make the following not specific to the object model.
- if (!component.isPrimitive()) {
- return 4;
- } else if (component == long.class || component == double.class) {
- return 8;
- } else if (component == int.class || component == float.class) {
- return 4;
- } else if (component == char.class || component == short.class) {
- return 2;
- } else {
- // component == byte.class || component == boolean.class.
- return 1;
- }
+ return getArrayIndexScaleForComponentType(component);
}
+ private static native int getArrayBaseOffsetForComponentType(Class component_class);
+ private static native int getArrayIndexScaleForComponentType(Class component_class);
+
/**
* Performs a compare-and-set operation on an <code>int</code>
* field within the given object.
diff --git a/libdvm/src/main/java/dalvik/system/VMRuntime.java b/libdvm/src/main/java/dalvik/system/VMRuntime.java
index f4acf47..3e4c160 100644
--- a/libdvm/src/main/java/dalvik/system/VMRuntime.java
+++ b/libdvm/src/main/java/dalvik/system/VMRuntime.java
@@ -223,6 +223,57 @@
public native Object newNonMovableArray(Class<?> componentType, int length);
/**
+ * Returns an array of at least minLength, but potentially larger. The increased size comes from
+ * avoiding any padding after the array. The amount of padding varies depending on the
+ * componentType and the memory allocator implementation.
+ */
+ public Object newUnpaddedArray(Class<?> componentType, int minLength) {
+ // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc,
+ // allocations are 8byte aligned so having 4bytes of array data avoids padding.
+ if (!componentType.isPrimitive()) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return java.lang.reflect.Array.newInstance(componentType, size);
+ } else if (componentType == char.class) {
+ int bytes = 20 + (2 * minLength);
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes / 2;
+ return new char[size];
+ } else if (componentType == int.class) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return new int[size];
+ } else if (componentType == byte.class) {
+ int bytes = 20 + minLength;
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes;
+ return new byte[size];
+ } else if (componentType == boolean.class) {
+ int bytes = 20 + minLength;
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes;
+ return new boolean[size];
+ } else if (componentType == short.class) {
+ int bytes = 20 + (2 * minLength);
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes / 2;
+ return new short[size];
+ } else if (componentType == float.class) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return new float[size];
+ } else if (componentType == long.class) {
+ return new long[minLength];
+ } else if (componentType == double.class) {
+ return new double[minLength];
+ } else {
+ assert componentType == void.class;
+ throw new IllegalArgumentException("Can't allocate an array of void");
+ }
+ }
+
+ /**
* Returns the address of array[0]. This differs from using JNI in that JNI might lie and
* give you the address of a copy of the array when in forcecopy mode.
*/
@@ -269,7 +320,7 @@
/**
* Register application info
*/
- public static void registerAppInfo(String appDir, String processName) {
+ public static void registerAppInfo(String appDir, String processName, String pkgname) {
// Nothing to do in dalvik.
}
}
diff --git a/libdvm/src/main/java/java/lang/Object.java b/libdvm/src/main/java/java/lang/Object.java
index 4bca034..d2cd2f1 100644
--- a/libdvm/src/main/java/java/lang/Object.java
+++ b/libdvm/src/main/java/java/lang/Object.java
@@ -266,10 +266,9 @@
* that called {@code notify()} has to release the object's monitor first.
* Also, the chosen thread still has to compete against other threads that
* try to synchronize on the same object.
- * <p>
- * This method can only be invoked by a thread which owns this object's
+ *
+ * <p>This method can only be invoked by a thread which owns this object's
* monitor. A thread becomes owner of an object's monitor
- * </p>
* <ul>
* <li>by executing a synchronized method of that object;</li>
* <li>by executing the body of a {@code synchronized} statement that
@@ -292,10 +291,9 @@
* will not run immediately. The thread that called {@code notify()} has to
* release the object's monitor first. Also, the threads still have to
* compete against other threads that try to synchronize on the same object.
- * <p>
- * This method can only be invoked by a thread which owns this object's
+ *
+ * <p>This method can only be invoked by a thread which owns this object's
* monitor. A thread becomes owner of an object's monitor
- * </p>
* <ul>
* <li>by executing a synchronized method of that object;</li>
* <li>by executing the body of a {@code synchronized} statement that
@@ -337,23 +335,22 @@
* notify()} or {@code notifyAll()} method of this object. This method can
* only be invoked by a thread which owns this object's monitor; see
* {@link #notify()} on how a thread can become the owner of a monitor.
- * <p>
- * A waiting thread can be sent {@code interrupt()} to cause it to
+ *
+ * <p>A waiting thread can be sent {@code interrupt()} to cause it to
* prematurely stop waiting, so {@code wait} should be called in a loop to
* check that the condition that has been waited for has been met before
* continuing.
- * </p>
- * <p>
- * While the thread waits, it gives up ownership of this object's monitor.
- * When it is notified (or interrupted), it re-acquires the monitor before
- * it starts running.
- * </p>
+ *
+ * <p>While the thread waits, it gives up ownership of this object's
+ * monitor. When it is notified (or interrupted), it re-acquires the monitor
+ * before it starts running.
*
* @throws IllegalMonitorStateException
* if the thread calling this method is not the owner of this
* object's monitor.
- * @throws InterruptedException
- * if another thread interrupts this thread while it is waiting.
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see #notify
* @see #notifyAll
* @see #wait(long)
@@ -370,17 +367,18 @@
* specified timeout expires. This method can only be invoked by a thread
* which owns this object's monitor; see {@link #notify()} on how a thread
* can become the owner of a monitor.
- * <p>
- * A waiting thread can be sent {@code interrupt()} to cause it to
+ *
+ * <p>A waiting thread can be sent {@code interrupt()} to cause it to
* prematurely stop waiting, so {@code wait} should be called in a loop to
* check that the condition that has been waited for has been met before
* continuing.
- * </p>
- * <p>
- * While the thread waits, it gives up ownership of this object's monitor.
- * When it is notified (or interrupted), it re-acquires the monitor before
- * it starts running.
- * </p>
+ *
+ * <p>While the thread waits, it gives up ownership of this object's
+ * monitor. When it is notified (or interrupted), it re-acquires the monitor
+ * before it starts running.
+ *
+ * <p>A timeout of zero means the calling thread should wait forever unless interrupted or
+ * notified.
*
* @param millis
* the maximum time to wait in milliseconds.
@@ -389,8 +387,9 @@
* @throws IllegalMonitorStateException
* if the thread calling this method is not the owner of this
* object's monitor.
- * @throws InterruptedException
- * if another thread interrupts this thread while it is waiting.
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see #notify
* @see #notifyAll
* @see #wait()
@@ -407,17 +406,18 @@
* specified timeout expires. This method can only be invoked by a thread
* that owns this object's monitor; see {@link #notify()} on how a thread
* can become the owner of a monitor.
- * <p>
- * A waiting thread can be sent {@code interrupt()} to cause it to
+ *
+ * <p>A waiting thread can be sent {@code interrupt()} to cause it to
* prematurely stop waiting, so {@code wait} should be called in a loop to
* check that the condition that has been waited for has been met before
* continuing.
- * </p>
- * <p>
- * While the thread waits, it gives up ownership of this object's monitor.
- * When it is notified (or interrupted), it re-acquires the monitor before
- * it starts running.
- * </p>
+ *
+ * <p>While the thread waits, it gives up ownership of this object's
+ * monitor. When it is notified (or interrupted), it re-acquires the monitor
+ * before it starts running.
+ *
+ * <p>A timeout of zero means the calling thread should wait forever unless interrupted or
+ * notified.
*
* @param millis
* the maximum time to wait in milliseconds.
@@ -430,8 +430,9 @@
* @throws IllegalMonitorStateException
* if the thread calling this method is not the owner of this
* object's monitor.
- * @throws InterruptedException
- * if another thread interrupts this thread while it is waiting.
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see #notify
* @see #notifyAll
* @see #wait()
diff --git a/libdvm/src/main/java/java/lang/Thread.java b/libdvm/src/main/java/java/lang/Thread.java
index ee4bdbe..a40dde1 100644
--- a/libdvm/src/main/java/java/lang/Thread.java
+++ b/libdvm/src/main/java/java/lang/Thread.java
@@ -732,8 +732,9 @@
* Blocks the current Thread (<code>Thread.currentThread()</code>) until
* the receiver finishes its execution and dies.
*
- * @throws InterruptedException if <code>interrupt()</code> was called for
- * the receiver while it was in the <code>join()</code> call
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception is
+ * thrown.
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
@@ -755,9 +756,12 @@
* the receiver finishes its execution and dies or the specified timeout
* expires, whatever happens first.
*
+ * <p>A timeout of zero means the calling thread should wait forever unless interrupted.
+ *
* @param millis The maximum time to wait (in milliseconds).
- * @throws InterruptedException if <code>interrupt()</code> was called for
- * the receiver while it was in the <code>join()</code> call
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception is
+ * thrown.
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
@@ -770,10 +774,13 @@
* the receiver finishes its execution and dies or the specified timeout
* expires, whatever happens first.
*
+ * <p>A timeout of zero means the calling thread should wait forever unless interrupted.
+ *
* @param millis The maximum time to wait (in milliseconds).
* @param nanos Extra nanosecond precision
- * @throws InterruptedException if <code>interrupt()</code> was called for
- * the receiver while it was in the <code>join()</code> call
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception is
+ * thrown.
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
@@ -986,9 +993,9 @@
*
* @param time
* The time to sleep in milliseconds.
- * @throws InterruptedException
- * if <code>interrupt()</code> was called for this Thread while
- * it was sleeping
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see Thread#interrupt()
*/
public static void sleep(long time) throws InterruptedException {
@@ -1004,9 +1011,9 @@
* The time to sleep in milliseconds.
* @param nanos
* Extra nanosecond precision
- * @throws InterruptedException
- * if <code>interrupt()</code> was called for this Thread while
- * it was sleeping
+ * @throws InterruptedException if the current thread has been interrupted.
+ * The interrupted status of the current thread will be cleared before the exception
+ * is thrown.
* @see Thread#interrupt()
*/
public static void sleep(long millis, int nanos) throws InterruptedException {
diff --git a/luni/src/main/java/android/system/ErrnoException.java b/luni/src/main/java/android/system/ErrnoException.java
new file mode 100644
index 0000000..b434df8
--- /dev/null
+++ b/luni/src/main/java/android/system/ErrnoException.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 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.io.IOException;
+import java.net.SocketException;
+import libcore.io.Libcore;
+
+/**
+ * A checked exception thrown when {@link Os} methods fail. This exception contains the native
+ * errno value, for comparison against the constants in {@link OsConstants}, should sophisticated
+ * callers need to adjust their behavior based on the exact failure.
+ *
+ * @hide
+ */
+public final class ErrnoException extends Exception {
+ private final String functionName;
+ public final int errno;
+
+ public ErrnoException(String functionName, int errno) {
+ this.functionName = functionName;
+ this.errno = errno;
+ }
+
+ public ErrnoException(String functionName, int errno, Throwable cause) {
+ super(cause);
+ this.functionName = functionName;
+ this.errno = errno;
+ }
+
+ /**
+ * Converts the stashed function name and errno value to a human-readable string.
+ * We do this here rather than in the constructor so that callers only pay for
+ * this if they need it.
+ */
+ @Override public String getMessage() {
+ String errnoName = OsConstants.errnoName(errno);
+ if (errnoName == null) {
+ errnoName = "errno " + errno;
+ }
+ String description = Libcore.os.strerror(errno);
+ return functionName + " failed: " + errnoName + " (" + description + ")";
+ }
+
+ public IOException rethrowAsIOException() throws IOException {
+ IOException newException = new IOException(getMessage());
+ newException.initCause(this);
+ throw newException;
+ }
+
+ public SocketException rethrowAsSocketException() throws SocketException {
+ throw new SocketException(getMessage(), this);
+ }
+}
diff --git a/luni/src/main/java/android/system/GaiException.java b/luni/src/main/java/android/system/GaiException.java
new file mode 100644
index 0000000..803fc29
--- /dev/null
+++ b/luni/src/main/java/android/system/GaiException.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 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.UnknownHostException;
+import libcore.io.Libcore;
+
+/**
+ * An unchecked exception thrown when {@code getaddrinfo} or {@code getnameinfo} fails.
+ * This exception contains the native {@link #error} value, should sophisticated
+ * callers need to adjust their behavior based on the exact failure.
+ *
+ * @hide
+ */
+public final class GaiException extends RuntimeException {
+ private final String functionName;
+
+ /**
+ * The native error value, for comparison with the {@code GAI_} constants in {@link OsConstants}.
+ */
+ public final int error;
+
+ public GaiException(String functionName, int error) {
+ this.functionName = functionName;
+ this.error = error;
+ }
+
+ public GaiException(String functionName, int error, Throwable cause) {
+ super(cause);
+ this.functionName = functionName;
+ this.error = error;
+ }
+
+ /**
+ * Converts the stashed function name and error value to a human-readable string.
+ * We do this here rather than in the constructor so that callers only pay for
+ * this if they need it.
+ */
+ @Override public String getMessage() {
+ String gaiName = OsConstants.gaiName(error);
+ if (gaiName == null) {
+ gaiName = "GAI_ error " + error;
+ }
+ String description = Libcore.os.gai_strerror(error);
+ return functionName + " failed: " + gaiName + " (" + description + ")";
+ }
+
+ public UnknownHostException rethrowAsUnknownHostException(String detailMessage) throws UnknownHostException {
+ UnknownHostException newException = new UnknownHostException(detailMessage);
+ newException.initCause(this);
+ throw newException;
+ }
+
+ public UnknownHostException rethrowAsUnknownHostException() throws UnknownHostException {
+ throw rethrowAsUnknownHostException(getMessage());
+ }
+}
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
new file mode 100644
index 0000000..d280ea8
--- /dev/null
+++ b/luni/src/main/java/android/system/Os.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2011 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 android.system.ErrnoException;
+import android.system.GaiException;
+import android.system.StructAddrinfo;
+import android.system.StructFlock;
+import android.system.StructGroupReq;
+import android.system.StructGroupSourceReq;
+import android.system.StructLinger;
+import android.system.StructPasswd;
+import android.system.StructPollfd;
+import android.system.StructStat;
+import android.system.StructStatVfs;
+import android.system.StructTimeval;
+import android.system.StructUcred;
+import android.system.StructUtsname;
+import android.util.MutableInt;
+import android.util.MutableLong;
+import java.io.FileDescriptor;
+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 libcore.io.Libcore;
+
+/**
+ * @hide
+ */
+public final class Os {
+ private Os() {}
+
+ public static FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
+ public static boolean access(String path, int mode) throws ErrnoException { return Libcore.os.access(path, mode); }
+ public static void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { Libcore.os.bind(fd, address, port); }
+ public static void chmod(String path, int mode) throws ErrnoException { Libcore.os.chmod(path, mode); }
+ public static void chown(String path, int uid, int gid) throws ErrnoException { Libcore.os.chown(path, uid, gid); }
+ public static void close(FileDescriptor fd) throws ErrnoException { Libcore.os.close(fd); }
+ public static void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { Libcore.os.connect(fd, address, port); }
+ public static FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException { return Libcore.os.dup(oldFd); }
+ public static FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException { return Libcore.os.dup2(oldFd, newFd); }
+ public static String[] environ() { return Libcore.os.environ(); }
+ public static void execv(String filename, String[] argv) throws ErrnoException { Libcore.os.execv(filename, argv); }
+ public static void execve(String filename, String[] argv, String[] envp) throws ErrnoException { Libcore.os.execve(filename, argv, envp); }
+ public static void fchmod(FileDescriptor fd, int mode) throws ErrnoException { Libcore.os.fchmod(fd, mode); }
+ public static void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { Libcore.os.fchown(fd, uid, gid); }
+ public static int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return Libcore.os.fcntlVoid(fd, cmd); }
+ public static int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return Libcore.os.fcntlLong(fd, cmd, arg); }
+ public static int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException { return Libcore.os.fcntlFlock(fd, cmd, arg); }
+ public static void fdatasync(FileDescriptor fd) throws ErrnoException { Libcore.os.fdatasync(fd); }
+ public static StructStat fstat(FileDescriptor fd) throws ErrnoException { return Libcore.os.fstat(fd); }
+ public static StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException { return Libcore.os.fstatvfs(fd); }
+ public static void fsync(FileDescriptor fd) throws ErrnoException { Libcore.os.fsync(fd); }
+ public static void ftruncate(FileDescriptor fd, long length) throws ErrnoException { Libcore.os.ftruncate(fd, length); }
+ public static String gai_strerror(int error) { return Libcore.os.gai_strerror(error); }
+ public static InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException { return Libcore.os.getaddrinfo(node, hints); }
+ public static int getegid() { return Libcore.os.getegid(); }
+ public static int geteuid() { return Libcore.os.geteuid(); }
+ public static int getgid() { return Libcore.os.getgid(); }
+ public static String getenv(String name) { return Libcore.os.getenv(name); }
+ public static String getnameinfo(InetAddress address, int flags) throws GaiException { return Libcore.os.getnameinfo(address, flags); }
+ public static SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return Libcore.os.getpeername(fd); }
+ public static int getpid() { return Libcore.os.getpid(); }
+ public static int getppid() { return Libcore.os.getppid(); }
+ public static StructPasswd getpwnam(String name) throws ErrnoException { return Libcore.os.getpwnam(name); }
+ public static StructPasswd getpwuid(int uid) throws ErrnoException { return Libcore.os.getpwuid(uid); }
+ public static SocketAddress getsockname(FileDescriptor fd) throws ErrnoException { return Libcore.os.getsockname(fd); }
+ public static int getsockoptByte(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptByte(fd, level, option); }
+ public static InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptInAddr(fd, level, option); }
+ public static int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptInt(fd, level, option); }
+ public static StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptLinger(fd, level, option); }
+ public static StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptTimeval(fd, level, option); }
+ public static StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptUcred(fd, level, option); }
+ public static int gettid() { return Libcore.os.gettid(); }
+ public static int getuid() { return Libcore.os.getuid(); }
+ public static String if_indextoname(int index) { return Libcore.os.if_indextoname(index); }
+ public static InetAddress inet_pton(int family, String address) { return Libcore.os.inet_pton(family, address); }
+ public static InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException { return Libcore.os.ioctlInetAddress(fd, cmd, interfaceName); }
+ public static int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException { return Libcore.os.ioctlInt(fd, cmd, arg); }
+ public static boolean isatty(FileDescriptor fd) { return Libcore.os.isatty(fd); }
+ public static void kill(int pid, int signal) throws ErrnoException { Libcore.os.kill(pid, signal); }
+ public static void lchown(String path, int uid, int gid) throws ErrnoException { Libcore.os.lchown(path, uid, gid); }
+ public static void listen(FileDescriptor fd, int backlog) throws ErrnoException { Libcore.os.listen(fd, backlog); }
+ public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return Libcore.os.lseek(fd, offset, whence); }
+ public static StructStat lstat(String path) throws ErrnoException { return Libcore.os.lstat(path); }
+ public static void mincore(long address, long byteCount, byte[] vector) throws ErrnoException { Libcore.os.mincore(address, byteCount, vector); }
+ public static void mkdir(String path, int mode) throws ErrnoException { Libcore.os.mkdir(path, mode); }
+ public static void mkfifo(String path, int mode) throws ErrnoException { Libcore.os.mkfifo(path, mode); }
+ public static void mlock(long address, long byteCount) throws ErrnoException { Libcore.os.mlock(address, byteCount); }
+ public static long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException { return Libcore.os.mmap(address, byteCount, prot, flags, fd, offset); }
+ public static void msync(long address, long byteCount, int flags) throws ErrnoException { Libcore.os.msync(address, byteCount, flags); }
+ public static void munlock(long address, long byteCount) throws ErrnoException { Libcore.os.munlock(address, byteCount); }
+ public static void munmap(long address, long byteCount) throws ErrnoException { Libcore.os.munmap(address, byteCount); }
+ public static FileDescriptor open(String path, int flags, int mode) throws ErrnoException { return Libcore.os.open(path, flags, mode); }
+ public static FileDescriptor[] pipe() throws ErrnoException { return Libcore.os.pipe(); }
+ public static int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return Libcore.os.poll(fds, timeoutMs); }
+ public static void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { Libcore.os.posix_fallocate(fd, offset, length); }
+ public static int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException { return Libcore.os.prctl(option, arg2, arg3, arg4, arg5); };
+ public static int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return Libcore.os.pread(fd, buffer, offset); }
+ public static int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return Libcore.os.pread(fd, bytes, byteOffset, byteCount, offset); }
+ public static int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return Libcore.os.pwrite(fd, buffer, offset); }
+ public static int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return Libcore.os.pwrite(fd, bytes, byteOffset, byteCount, offset); }
+ public static int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return Libcore.os.read(fd, buffer); }
+ public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return Libcore.os.read(fd, bytes, byteOffset, byteCount); }
+ public static String readlink(String path) throws ErrnoException { return Libcore.os.readlink(path); }
+ public static int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return Libcore.os.readv(fd, buffers, offsets, byteCounts); }
+ public static int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return Libcore.os.recvfrom(fd, buffer, flags, srcAddress); }
+ public static int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
+ public static void remove(String path) throws ErrnoException { Libcore.os.remove(path); }
+ public static void rename(String oldPath, String newPath) throws ErrnoException { Libcore.os.rename(oldPath, newPath); }
+ public static long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return Libcore.os.sendfile(outFd, inFd, inOffset, byteCount); }
+ public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, buffer, flags, inetAddress, port); }
+ public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
+ public static void setegid(int egid) throws ErrnoException { Libcore.os.setegid(egid); }
+ public static void setenv(String name, String value, boolean overwrite) throws ErrnoException { Libcore.os.setenv(name, value, overwrite); }
+ public static void seteuid(int euid) throws ErrnoException { Libcore.os.seteuid(euid); }
+ public static void setgid(int gid) throws ErrnoException { Libcore.os.setgid(gid); }
+ public static int setsid() throws ErrnoException { return Libcore.os.setsid(); }
+ public static void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptByte(fd, level, option, value); }
+ public static void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { Libcore.os.setsockoptIfreq(fd, level, option, value); }
+ public static void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptInt(fd, level, option, value); }
+ public static void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptIpMreqn(fd, level, option, value); }
+ public static void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException { Libcore.os.setsockoptGroupReq(fd, level, option, value); }
+ public static void setsockoptGroupSourceReq(FileDescriptor fd, int level, int option, StructGroupSourceReq value) throws ErrnoException { Libcore.os.setsockoptGroupSourceReq(fd, level, option, value); }
+ public static void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException { Libcore.os.setsockoptLinger(fd, level, option, value); }
+ public static void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException { Libcore.os.setsockoptTimeval(fd, level, option, value); }
+ public static void setuid(int uid) throws ErrnoException { Libcore.os.setuid(uid); }
+ public static void shutdown(FileDescriptor fd, int how) throws ErrnoException { Libcore.os.shutdown(fd, how); }
+ public static FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { return Libcore.os.socket(domain, type, protocol); }
+ public static void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { Libcore.os.socketpair(domain, type, protocol, fd1, fd2); }
+ public static StructStat stat(String path) throws ErrnoException { return Libcore.os.stat(path); }
+ public static StructStatVfs statvfs(String path) throws ErrnoException { return Libcore.os.statvfs(path); }
+ public static String strerror(int errno) { return Libcore.os.strerror(errno); }
+ public static String strsignal(int signal) { return Libcore.os.strsignal(signal); }
+ public static void symlink(String oldPath, String newPath) throws ErrnoException { Libcore.os.symlink(oldPath, newPath); }
+ public static long sysconf(int name) { return Libcore.os.sysconf(name); }
+ public static void tcdrain(FileDescriptor fd) throws ErrnoException { Libcore.os.tcdrain(fd); }
+ public static void tcsendbreak(FileDescriptor fd, int duration) throws ErrnoException { Libcore.os.tcsendbreak(fd, duration); }
+ public static int umask(int mask) { return Libcore.os.umask(mask); }
+ public static StructUtsname uname() { return Libcore.os.uname(); }
+ public static void unsetenv(String name) throws ErrnoException { Libcore.os.unsetenv(name); }
+ public static int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return Libcore.os.waitpid(pid, status, options); }
+ public static int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return Libcore.os.write(fd, buffer); }
+ public static int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return Libcore.os.write(fd, bytes, byteOffset, byteCount); }
+ public static int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return Libcore.os.writev(fd, buffers, offsets, byteCounts); }
+}
diff --git a/luni/src/main/java/libcore/io/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
similarity index 98%
rename from luni/src/main/java/libcore/io/OsConstants.java
rename to luni/src/main/java/android/system/OsConstants.java
index 0eb5850..3bf70f6 100644
--- a/luni/src/main/java/libcore/io/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-package libcore.io;
+package android.system;
+/**
+ * @hide
+ */
public final class OsConstants {
private OsConstants() { }
@@ -250,6 +253,10 @@
public static final int MAP_SHARED = placeholder();
public static final int MCAST_JOIN_GROUP = placeholder();
public static final int MCAST_LEAVE_GROUP = placeholder();
+ public static final int MCAST_JOIN_SOURCE_GROUP = placeholder();
+ public static final int MCAST_LEAVE_SOURCE_GROUP = placeholder();
+ public static final int MCAST_BLOCK_SOURCE = placeholder();
+ public static final int MCAST_UNBLOCK_SOURCE = placeholder();
public static final int MCL_CURRENT = placeholder();
public static final int MCL_FUTURE = placeholder();
public static final int MSG_CTRUNC = placeholder();
@@ -289,6 +296,7 @@
public static final int POLLRDNORM = placeholder();
public static final int POLLWRBAND = placeholder();
public static final int POLLWRNORM = placeholder();
+ public static final int PR_SET_NO_NEW_PRIVS = placeholder();
public static final int PROT_EXEC = placeholder();
public static final int PROT_NONE = placeholder();
public static final int PROT_READ = placeholder();
diff --git a/luni/src/main/java/android/system/StructAddrinfo.java b/luni/src/main/java/android/system/StructAddrinfo.java
new file mode 100644
index 0000000..2425946
--- /dev/null
+++ b/luni/src/main/java/android/system/StructAddrinfo.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 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.InetAddress;
+import libcore.util.Objects;
+
+/**
+ * Information returned/taken by getaddrinfo(3). Corresponds to C's {@code struct addrinfo} from
+ * <a href="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/netdb.h.html"><netdb.h></a>
+ *
+ * TODO: we currently only _take_ a StructAddrinfo; getaddrinfo returns an InetAddress[].
+ *
+ * @hide
+ */
+public final class StructAddrinfo {
+ /** Flags describing the kind of lookup to be done. (Such as AI_ADDRCONFIG.) */
+ public int ai_flags;
+
+ /** Desired address family for results. (Such as AF_INET6 for IPv6. AF_UNSPEC means "any".) */
+ public int ai_family;
+
+ /** Socket type. (Such as SOCK_DGRAM. 0 means "any".) */
+ public int ai_socktype;
+
+ /** Protocol. (Such as IPPROTO_IPV6 IPv6. 0 means "any".) */
+ public int ai_protocol;
+
+ /** Address length. (Not useful in Java.) */
+ // public int ai_addrlen;
+
+ /** Address. */
+ public InetAddress ai_addr;
+
+ /** Canonical name of service location (if AI_CANONNAME provided in ai_flags). */
+ // public String ai_canonname;
+
+ /** Next element in linked list. */
+ public StructAddrinfo ai_next;
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
+}
diff --git a/luni/src/main/java/libcore/io/StructFlock.java b/luni/src/main/java/android/system/StructFlock.java
similarity index 61%
rename from luni/src/main/java/libcore/io/StructFlock.java
rename to luni/src/main/java/android/system/StructFlock.java
index 11c29df..92cd95a 100644
--- a/luni/src/main/java/libcore/io/StructFlock.java
+++ b/luni/src/main/java/android/system/StructFlock.java
@@ -14,26 +14,34 @@
* limitations under the License.
*/
-package libcore.io;
+package android.system;
+
+import libcore.util.Objects;
/**
* Information returned/taken by fcntl(2) F_GETFL and F_SETFL. Corresponds to C's
* {@code struct flock} from
* <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/fcntl.h.html"><fcntl.h></a>
+ *
+ * @hide
*/
public final class StructFlock {
- /** The operation type, one of F_RDLCK, F_WRLCK, or F_UNLCK. */
- public short l_type;
+ /** The operation type, one of F_RDLCK, F_WRLCK, or F_UNLCK. */
+ public short l_type;
- /** How to interpret l_start, one of SEEK_CUR, SEEK_END, SEEK_SET. */
- public short l_whence;
+ /** How to interpret l_start, one of SEEK_CUR, SEEK_END, SEEK_SET. */
+ public short l_whence;
- /** Start offset. */
- public long l_start; /*off_t*/
+ /** Start offset. */
+ public long l_start; /*off_t*/
- /** Byte count to operate on. */
- public long l_len; /*off_t*/
+ /** Byte count to operate on. */
+ public long l_len; /*off_t*/
- /** Process blocking our lock (filled in by F_GETLK, otherwise unused). */
- public int l_pid; /*pid_t*/
+ /** Process blocking our lock (filled in by F_GETLK, otherwise unused). */
+ public int l_pid; /*pid_t*/
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
}
diff --git a/luni/src/main/java/libcore/io/StructGroupReq.java b/luni/src/main/java/android/system/StructGroupReq.java
similarity index 65%
rename from luni/src/main/java/libcore/io/StructGroupReq.java
rename to luni/src/main/java/android/system/StructGroupReq.java
index 0bdf783..8ed5950 100644
--- a/luni/src/main/java/libcore/io/StructGroupReq.java
+++ b/luni/src/main/java/android/system/StructGroupReq.java
@@ -14,23 +14,26 @@
* limitations under the License.
*/
-package libcore.io;
+package android.system;
import java.net.InetAddress;
+import libcore.util.Objects;
/**
* Corresponds to C's {@code struct group_req}.
+ *
+ * @hide
*/
public final class StructGroupReq {
- public final int gr_interface;
- public final InetAddress gr_group;
+ public final int gr_interface;
+ public final InetAddress gr_group;
- public StructGroupReq(int gr_interface, InetAddress gr_group) {
- this.gr_interface = gr_interface;
- this.gr_group = gr_group;
- }
+ public StructGroupReq(int gr_interface, InetAddress gr_group) {
+ this.gr_interface = gr_interface;
+ this.gr_group = gr_group;
+ }
- @Override public String toString() {
- return "StructGroupReq[gr_interface=" + gr_interface + ",gr_group=" + gr_group + "]";
- }
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
}
diff --git a/luni/src/main/java/android/system/StructGroupSourceReq.java b/luni/src/main/java/android/system/StructGroupSourceReq.java
new file mode 100644
index 0000000..c300338
--- /dev/null
+++ b/luni/src/main/java/android/system/StructGroupSourceReq.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 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.InetAddress;
+import libcore.util.Objects;
+
+/**
+ * Corresponds to C's {@code struct group_source_req}.
+ *
+ * @hide
+ */
+public final class StructGroupSourceReq {
+ public final int gsr_interface;
+ public final InetAddress gsr_group;
+ public final InetAddress gsr_source;
+
+ public StructGroupSourceReq(int gsr_interface, InetAddress gsr_group, InetAddress gsr_source) {
+ this.gsr_interface = gsr_interface;
+ this.gsr_group = gsr_group;
+ this.gsr_source = gsr_source;
+ }
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
+}
diff --git a/luni/src/main/java/libcore/io/StructLinger.java b/luni/src/main/java/android/system/StructLinger.java
similarity index 62%
rename from luni/src/main/java/libcore/io/StructLinger.java
rename to luni/src/main/java/android/system/StructLinger.java
index 9f149af..55ffc5c 100644
--- a/luni/src/main/java/libcore/io/StructLinger.java
+++ b/luni/src/main/java/android/system/StructLinger.java
@@ -14,29 +14,33 @@
* limitations under the License.
*/
-package libcore.io;
+package android.system;
+
+import libcore.util.Objects;
/**
* Corresponds to C's {@code struct linger} from
* <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html"><sys/socket.h></a>
+ *
+ * @hide
*/
public final class StructLinger {
- /** Whether or not linger is enabled. Non-zero is on. */
- public final int l_onoff;
+ /** Whether or not linger is enabled. Non-zero is on. */
+ public final int l_onoff;
- /** Linger time in seconds. */
- public final int l_linger;
+ /** Linger time in seconds. */
+ public final int l_linger;
- public StructLinger(int l_onoff, int l_linger) {
- this.l_onoff = l_onoff;
- this.l_linger = l_linger;
- }
+ public StructLinger(int l_onoff, int l_linger) {
+ this.l_onoff = l_onoff;
+ this.l_linger = l_linger;
+ }
- public boolean isOn() {
- return l_onoff != 0;
- }
+ public boolean isOn() {
+ return l_onoff != 0;
+ }
- @Override public String toString() {
- return "StructLinger[l_onoff=" + l_onoff + ",l_linger=" + l_linger + "]";
- }
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
}
diff --git a/luni/src/main/java/libcore/io/StructPasswd.java b/luni/src/main/java/android/system/StructPasswd.java
similarity index 62%
rename from luni/src/main/java/libcore/io/StructPasswd.java
rename to luni/src/main/java/android/system/StructPasswd.java
index 6f5e058..303973e 100644
--- a/luni/src/main/java/libcore/io/StructPasswd.java
+++ b/luni/src/main/java/android/system/StructPasswd.java
@@ -14,25 +14,33 @@
* limitations under the License.
*/
-package libcore.io;
+package android.system;
+
+import libcore.util.Objects;
/**
* Information returned by getpwnam(3) and getpwuid(3). Corresponds to C's
* {@code struct passwd} from
* <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/pwd.h.html"><pwd.h></a>
+ *
+ * @hide
*/
public final class StructPasswd {
- public String pw_name;
- public int pw_uid; /* uid_t */
- public int pw_gid; /* gid_t */
- public String pw_dir;
- public String pw_shell;
+ public final String pw_name;
+ public final int pw_uid;
+ public final int pw_gid;
+ public final String pw_dir;
+ public final String pw_shell;
- public StructPasswd(String pw_name, int pw_uid, int pw_gid, String pw_dir, String pw_shell) {
- this.pw_name = pw_name;
- this.pw_uid = pw_uid;
- this.pw_gid = pw_gid;
- this.pw_dir = pw_dir;
- this.pw_shell = pw_shell;
- }
+ public StructPasswd(String pw_name, int pw_uid, int pw_gid, String pw_dir, String pw_shell) {
+ this.pw_name = pw_name;
+ this.pw_uid = pw_uid;
+ this.pw_gid = pw_gid;
+ this.pw_dir = pw_dir;
+ this.pw_shell = pw_shell;
+ }
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
}
diff --git a/luni/src/main/java/android/system/StructPollfd.java b/luni/src/main/java/android/system/StructPollfd.java
new file mode 100644
index 0000000..b9526bf
--- /dev/null
+++ b/luni/src/main/java/android/system/StructPollfd.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 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.io.FileDescriptor;
+import libcore.util.Objects;
+
+/**
+ * Corresponds to C's {@code struct pollfd} from
+ * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/poll.h.html"><poll.h></a>
+ *
+ * @hide
+ */
+public final class StructPollfd {
+ /** The file descriptor to poll. */
+ public FileDescriptor fd;
+
+ /**
+ * The events we're interested in. POLLIN corresponds to being in select(2)'s read fd set,
+ * POLLOUT to the write fd set.
+ */
+ public short events;
+
+ /** The events that actually happened. */
+ public short revents;
+
+ /**
+ * A non-standard extension that lets callers conveniently map back to the object
+ * their fd belongs to. This is used by Selector, for example, to associate each
+ * FileDescriptor with the corresponding SelectionKey.
+ */
+ public Object userData;
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
+}
diff --git a/luni/src/main/java/android/system/StructStat.java b/luni/src/main/java/android/system/StructStat.java
new file mode 100644
index 0000000..92e8512
--- /dev/null
+++ b/luni/src/main/java/android/system/StructStat.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 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 libcore.util.Objects;
+
+/**
+ * File information returned by fstat(2), lstat(2), and stat(2). Corresponds to C's
+ * {@code struct stat} from
+ * <a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/stat.h.html"><stat.h></a>
+ *
+ * @hide
+ */
+public final class StructStat {
+ /** Device ID of device containing file. */
+ public final long st_dev; /*dev_t*/
+
+ /** File serial number (inode). */
+ public final long st_ino; /*ino_t*/
+
+ /** Mode (permissions) of file. */
+ public final int st_mode; /*mode_t*/
+
+ /** Number of hard links to the file. */
+ public final long st_nlink; /*nlink_t*/
+
+ /** User ID of file. */
+ public final int st_uid; /*uid_t*/
+
+ /** Group ID of file. */
+ public final int st_gid; /*gid_t*/
+
+ /** Device ID (if file is character or block special). */
+ public final long st_rdev; /*dev_t*/
+
+ /**
+ * For regular files, the file size in bytes.
+ * For symbolic links, the length in bytes of the pathname contained in the symbolic link.
+ * For a shared memory object, the length in bytes.
+ * For a typed memory object, the length in bytes.
+ * For other file types, the use of this field is unspecified.
+ */
+ public final long st_size; /*off_t*/
+
+ /** Time of last access. */
+ public final long st_atime; /*time_t*/
+
+ /** Time of last data modification. */
+ public final long st_mtime; /*time_t*/
+
+ /** Time of last status change. */
+ public final long st_ctime; /*time_t*/
+
+ /**
+ * A file system-specific preferred I/O block size for this object.
+ * For some file system types, this may vary from file to file.
+ */
+ public final long st_blksize; /*blksize_t*/
+
+ /** Number of blocks allocated for this object. */
+ public final long st_blocks; /*blkcnt_t*/
+
+ public StructStat(long st_dev, long st_ino, int st_mode, long st_nlink, int st_uid, int st_gid,
+ long st_rdev, long st_size, long st_atime, long st_mtime, long st_ctime,
+ long st_blksize, long st_blocks) {
+ this.st_dev = st_dev;
+ this.st_ino = st_ino;
+ this.st_mode = st_mode;
+ this.st_nlink = st_nlink;
+ this.st_uid = st_uid;
+ this.st_gid = st_gid;
+ this.st_rdev = st_rdev;
+ this.st_size = st_size;
+ this.st_atime = st_atime;
+ this.st_mtime = st_mtime;
+ this.st_ctime = st_ctime;
+ this.st_blksize = st_blksize;
+ this.st_blocks = st_blocks;
+ }
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
+}
diff --git a/luni/src/main/java/libcore/io/StructStatVfs.java b/luni/src/main/java/android/system/StructStatVfs.java
similarity index 89%
rename from luni/src/main/java/libcore/io/StructStatVfs.java
rename to luni/src/main/java/android/system/StructStatVfs.java
index bb78ff2..1d398af 100644
--- a/luni/src/main/java/libcore/io/StructStatVfs.java
+++ b/luni/src/main/java/android/system/StructStatVfs.java
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-package libcore.io;
+package android.system;
+
+import libcore.util.Objects;
/**
* File information returned by fstatvfs(2) and statvfs(2).
+ *
+ * @hide
*/
public final class StructStatVfs {
/** File system block size (used for block counts). */
@@ -53,7 +57,7 @@
/** Maximum filename length. */
public final long f_namemax; /*unsigned long*/
- StructStatVfs(long f_bsize, long f_frsize, long f_blocks, long f_bfree, long f_bavail,
+ public StructStatVfs(long f_bsize, long f_frsize, long f_blocks, long f_bfree, long f_bavail,
long f_files, long f_ffree, long f_favail,
long f_fsid, long f_flag, long f_namemax) {
this.f_bsize = f_bsize;
@@ -68,4 +72,8 @@
this.f_flag = f_flag;
this.f_namemax = f_namemax;
}
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
}
diff --git a/luni/src/main/java/android/system/StructTimeval.java b/luni/src/main/java/android/system/StructTimeval.java
new file mode 100644
index 0000000..8a155b4
--- /dev/null
+++ b/luni/src/main/java/android/system/StructTimeval.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 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 libcore.util.Objects;
+
+/**
+ * Corresponds to C's {@code struct timeval} from
+ * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html"><sys/time.h></a>
+ *
+ * @hide
+ */
+public final class StructTimeval {
+ /** Seconds. */
+ public final long tv_sec;
+
+ /** Microseconds. */
+ public final long tv_usec;
+
+ private StructTimeval(long tv_sec, long tv_usec) {
+ this.tv_sec = tv_sec;
+ this.tv_usec = tv_usec;
+ }
+
+ public static StructTimeval fromMillis(long millis) {
+ long tv_sec = millis / 1000;
+ long tv_usec = (millis - (tv_sec * 1000)) * 1000;
+ return new StructTimeval(tv_sec, tv_usec);
+ }
+
+ public long toMillis() {
+ return (tv_sec * 1000) + (tv_usec / 1000);
+ }
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
+}
diff --git a/luni/src/main/java/libcore/io/StructUcred.java b/luni/src/main/java/android/system/StructUcred.java
similarity index 86%
rename from luni/src/main/java/libcore/io/StructUcred.java
rename to luni/src/main/java/android/system/StructUcred.java
index 359995d..a1e3cd6 100644
--- a/luni/src/main/java/libcore/io/StructUcred.java
+++ b/luni/src/main/java/android/system/StructUcred.java
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-package libcore.io;
+package android.system;
+
+import libcore.util.Objects;
/**
* Corresponds to C's {@code struct ucred}.
+ *
+ * @hide
*/
public final class StructUcred {
/** The peer's process id. */
@@ -29,13 +33,13 @@
/** The peer process' gid. */
public final int gid;
- private StructUcred(int pid, int uid, int gid) {
+ public StructUcred(int pid, int uid, int gid) {
this.pid = pid;
this.uid = uid;
this.gid = gid;
}
@Override public String toString() {
- return "StructUcred[pid=" + pid + ",uid=" + uid + ",gid=" + gid + "]";
+ return Objects.toString(this);
}
}
diff --git a/luni/src/main/java/android/system/StructUtsname.java b/luni/src/main/java/android/system/StructUtsname.java
new file mode 100644
index 0000000..2b7937c
--- /dev/null
+++ b/luni/src/main/java/android/system/StructUtsname.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 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 libcore.util.Objects;
+
+/**
+ * Information returned by uname(2). Corresponds to C's
+ * {@code struct utsname} from
+ * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_utsname.h.html"><sys/utsname.h></a>
+ *
+ * @hide
+ */
+public final class StructUtsname {
+ /** The OS name, such as "Linux". */
+ public final String sysname;
+
+ /** The machine's unqualified name on some implementation-defined network. */
+ public final String nodename;
+
+ /** The OS release, such as "2.6.35-27-generic". */
+ public final String release;
+
+ /** The OS version, such as "#48-Ubuntu SMP Tue Feb 22 20:25:29 UTC 2011". */
+ public final String version;
+
+ /** The machine architecture, such as "armv7l" or "x86_64". */
+ public final String machine;
+
+ public StructUtsname(String sysname, String nodename, String release, String version, String machine) {
+ this.sysname = sysname;
+ this.nodename = nodename;
+ this.release = release;
+ this.version = version;
+ this.machine = machine;
+ }
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
+}
diff --git a/luni/src/main/java/libcore/util/MutableBoolean.java b/luni/src/main/java/android/util/MutableBoolean.java
similarity index 83%
rename from luni/src/main/java/libcore/util/MutableBoolean.java
rename to luni/src/main/java/android/util/MutableBoolean.java
index 359a8f9..90bf68c 100644
--- a/luni/src/main/java/libcore/util/MutableBoolean.java
+++ b/luni/src/main/java/android/util/MutableBoolean.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package libcore.util;
+package android.util;
+/**
+ * @hide
+ */
public final class MutableBoolean {
- public boolean value;
+ public boolean value;
- public MutableBoolean(boolean value) {
- this.value = value;
- }
+ public MutableBoolean(boolean value) {
+ this.value = value;
+ }
}
diff --git a/luni/src/main/java/libcore/util/MutableByte.java b/luni/src/main/java/android/util/MutableByte.java
similarity index 84%
rename from luni/src/main/java/libcore/util/MutableByte.java
rename to luni/src/main/java/android/util/MutableByte.java
index 13f780b..65738b9 100644
--- a/luni/src/main/java/libcore/util/MutableByte.java
+++ b/luni/src/main/java/android/util/MutableByte.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package libcore.util;
+package android.util;
+/**
+ * @hide
+ */
public final class MutableByte {
- public byte value;
+ public byte value;
- public MutableByte(byte value) {
- this.value = value;
- }
+ public MutableByte(byte value) {
+ this.value = value;
+ }
}
diff --git a/luni/src/main/java/libcore/util/MutableChar.java b/luni/src/main/java/android/util/MutableChar.java
similarity index 84%
rename from luni/src/main/java/libcore/util/MutableChar.java
rename to luni/src/main/java/android/util/MutableChar.java
index 1cafc3c..b59bab3 100644
--- a/luni/src/main/java/libcore/util/MutableChar.java
+++ b/luni/src/main/java/android/util/MutableChar.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package libcore.util;
+package android.util;
+/**
+ * @hide
+ */
public final class MutableChar {
- public char value;
+ public char value;
- public MutableChar(char value) {
- this.value = value;
- }
+ public MutableChar(char value) {
+ this.value = value;
+ }
}
diff --git a/luni/src/main/java/libcore/util/MutableDouble.java b/luni/src/main/java/android/util/MutableDouble.java
similarity index 83%
rename from luni/src/main/java/libcore/util/MutableDouble.java
rename to luni/src/main/java/android/util/MutableDouble.java
index 4473ae6..3e2cc3a 100644
--- a/luni/src/main/java/libcore/util/MutableDouble.java
+++ b/luni/src/main/java/android/util/MutableDouble.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package libcore.util;
+package android.util;
+/**
+ * @hide
+ */
public final class MutableDouble {
- public double value;
+ public double value;
- public MutableDouble(double value) {
- this.value = value;
- }
+ public MutableDouble(double value) {
+ this.value = value;
+ }
}
diff --git a/luni/src/main/java/libcore/util/MutableFloat.java b/luni/src/main/java/android/util/MutableFloat.java
similarity index 83%
rename from luni/src/main/java/libcore/util/MutableFloat.java
rename to luni/src/main/java/android/util/MutableFloat.java
index f81fba5..6e30501 100644
--- a/luni/src/main/java/libcore/util/MutableFloat.java
+++ b/luni/src/main/java/android/util/MutableFloat.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package libcore.util;
+package android.util;
+/**
+ * @hide
+ */
public final class MutableFloat {
- public float value;
+ public float value;
- public MutableFloat(float value) {
- this.value = value;
- }
+ public MutableFloat(float value) {
+ this.value = value;
+ }
}
diff --git a/luni/src/main/java/libcore/util/MutableInt.java b/luni/src/main/java/android/util/MutableInt.java
similarity index 84%
rename from luni/src/main/java/libcore/util/MutableInt.java
rename to luni/src/main/java/android/util/MutableInt.java
index c8feb3a..8220c44 100644
--- a/luni/src/main/java/libcore/util/MutableInt.java
+++ b/luni/src/main/java/android/util/MutableInt.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package libcore.util;
+package android.util;
+/**
+ * @hide
+ */
public final class MutableInt {
- public int value;
+ public int value;
- public MutableInt(int value) {
- this.value = value;
- }
+ public MutableInt(int value) {
+ this.value = value;
+ }
}
diff --git a/luni/src/main/java/libcore/util/MutableLong.java b/luni/src/main/java/android/util/MutableLong.java
similarity index 84%
rename from luni/src/main/java/libcore/util/MutableLong.java
rename to luni/src/main/java/android/util/MutableLong.java
index ad9b78e..5df6a0d 100644
--- a/luni/src/main/java/libcore/util/MutableLong.java
+++ b/luni/src/main/java/android/util/MutableLong.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package libcore.util;
+package android.util;
+/**
+ * @hide
+ */
public final class MutableLong {
- public long value;
+ public long value;
- public MutableLong(long value) {
- this.value = value;
- }
+ public MutableLong(long value) {
+ this.value = value;
+ }
}
diff --git a/luni/src/main/java/libcore/util/MutableShort.java b/luni/src/main/java/android/util/MutableShort.java
similarity index 83%
rename from luni/src/main/java/libcore/util/MutableShort.java
rename to luni/src/main/java/android/util/MutableShort.java
index 78b4c33..3880fef 100644
--- a/luni/src/main/java/libcore/util/MutableShort.java
+++ b/luni/src/main/java/android/util/MutableShort.java
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-package libcore.util;
+package android.util;
+/**
+ * @hide
+ */
public final class MutableShort {
- public short value;
+ public short value;
- public MutableShort(short value) {
- this.value = value;
- }
+ public MutableShort(short value) {
+ this.value = value;
+ }
}
diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java
index 123109b..d107c28 100644
--- a/luni/src/main/java/java/io/File.java
+++ b/luni/src/main/java/java/io/File.java
@@ -17,6 +17,9 @@
package java.io;
+import android.system.ErrnoException;
+import android.system.StructStat;
+import android.system.StructStatVfs;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -24,12 +27,9 @@
import java.util.List;
import java.util.Random;
import libcore.io.DeleteOnExit;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import libcore.io.StructStat;
-import libcore.io.StructStatVfs;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* An "abstract" representation of a file system entity identified by a
@@ -411,15 +411,10 @@
* if an I/O error occurs.
*/
public String getCanonicalPath() throws IOException {
- return realpath(getAbsolutePath());
+ return canonicalizePath(getAbsolutePath());
}
- /**
- * TODO: move this stuff to libcore.os.
- * @hide
- */
- private static native String realpath(String path);
- private static native String readlink(String path);
+ private static native String canonicalizePath(String path);
/**
* Returns a new file created using the canonical path of this file.
diff --git a/luni/src/main/java/java/io/FileDescriptor.java b/luni/src/main/java/java/io/FileDescriptor.java
index e4eb06cc..cb38123 100644
--- a/luni/src/main/java/java/io/FileDescriptor.java
+++ b/luni/src/main/java/java/io/FileDescriptor.java
@@ -17,9 +17,9 @@
package java.io;
-import libcore.io.ErrnoException;
+import android.system.ErrnoException;
import libcore.io.Libcore;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* Wraps a Unix file descriptor. It's possible to get the file descriptor used by some
diff --git a/luni/src/main/java/java/io/FileInputStream.java b/luni/src/main/java/java/io/FileInputStream.java
index 644f749..7944ef1 100644
--- a/luni/src/main/java/java/io/FileInputStream.java
+++ b/luni/src/main/java/java/io/FileInputStream.java
@@ -19,15 +19,13 @@
import dalvik.system.CloseGuard;
-import java.nio.NioUtils;
+import android.system.ErrnoException;
import java.nio.channels.FileChannel;
-import java.util.Arrays;
-import libcore.io.ErrnoException;
+import java.nio.NioUtils;
import libcore.io.IoBridge;
-import libcore.io.IoUtils;
import libcore.io.Libcore;
import libcore.io.Streams;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* An input stream that reads bytes from a file.
@@ -118,7 +116,7 @@
channel.close();
}
if (shouldClose) {
- IoUtils.close(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
} else {
// An owned fd has been invalidated by IoUtils.close, but
// we need to explicitly stop using an unowned fd (http://b/4361076).
diff --git a/luni/src/main/java/java/io/FileOutputStream.java b/luni/src/main/java/java/io/FileOutputStream.java
index f5ba11e..f91ee20 100644
--- a/luni/src/main/java/java/io/FileOutputStream.java
+++ b/luni/src/main/java/java/io/FileOutputStream.java
@@ -20,10 +20,9 @@
import dalvik.system.CloseGuard;
import java.nio.NioUtils;
import java.nio.channels.FileChannel;
-import java.util.Arrays;
import libcore.io.IoBridge;
-import libcore.io.IoUtils;
-import static libcore.io.OsConstants.*;
+
+import static android.system.OsConstants.*;
/**
* An output stream that writes bytes to a file. If the output file exists, it
@@ -136,7 +135,7 @@
channel.close();
}
if (shouldClose) {
- IoUtils.close(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
} else {
// An owned fd has been invalidated by IoUtils.close, but
// we need to explicitly stop using an unowned fd (http://b/4361076).
diff --git a/luni/src/main/java/java/io/OutputStreamWriter.java b/luni/src/main/java/java/io/OutputStreamWriter.java
index d69c87a..bc8710d 100644
--- a/luni/src/main/java/java/io/OutputStreamWriter.java
+++ b/luni/src/main/java/java/io/OutputStreamWriter.java
@@ -122,8 +122,8 @@
}
/**
- * Closes this writer. This implementation flushes the buffer as well as the
- * target stream. The target stream is then closed and the resources for the
+ * Closes this writer. This implementation flushes the buffer but <strong>does not</strong>
+ * flush the target stream. The target stream is then closed and the resources for the
* buffer and converter are released.
*
* <p>Only the first invocation of this method has any effect. Subsequent calls
diff --git a/luni/src/main/java/java/io/RandomAccessFile.java b/luni/src/main/java/java/io/RandomAccessFile.java
index 6e88fe0..da99765 100644
--- a/luni/src/main/java/java/io/RandomAccessFile.java
+++ b/luni/src/main/java/java/io/RandomAccessFile.java
@@ -17,19 +17,18 @@
package java.io;
+import android.system.ErrnoException;
import dalvik.system.CloseGuard;
import java.nio.ByteOrder;
-import java.nio.NioUtils;
import java.nio.channels.FileChannel;
import java.nio.charset.ModifiedUtf8;
+import java.nio.NioUtils;
import java.util.Arrays;
-import libcore.io.ErrnoException;
import libcore.io.IoBridge;
-import libcore.io.IoUtils;
import libcore.io.Libcore;
import libcore.io.Memory;
import libcore.io.SizeOf;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* Allows reading from and writing to a file in a random-access manner. This is
@@ -163,7 +162,7 @@
channel.close();
channel = null;
}
- IoUtils.close(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
}
}
diff --git a/luni/src/main/java/java/lang/CaseMapper.java b/luni/src/main/java/java/lang/CaseMapper.java
index 5ec41f5..4e411d1 100644
--- a/luni/src/main/java/java/lang/CaseMapper.java
+++ b/luni/src/main/java/java/lang/CaseMapper.java
@@ -18,6 +18,7 @@
import java.util.Locale;
import libcore.icu.ICU;
+import libcore.icu.Transliterator;
/**
* Performs case operations as described by http://unicode.org/reports/tr21/tr21-5.html.
@@ -45,6 +46,7 @@
*/
public static String toLowerCase(Locale locale, String s, char[] value, int offset, int count) {
// Punt hard cases to ICU4C.
+ // Note that Greek isn't a particularly hard case for toLowerCase, only toUpperCase.
String languageCode = locale.getLanguage();
if (languageCode.equals("tr") || languageCode.equals("az") || languageCode.equals("lt")) {
return ICU.toLowerCase(s, locale.toString());
@@ -139,11 +141,20 @@
return index;
}
+ private static final ThreadLocal<Transliterator> EL_UPPER = new ThreadLocal<Transliterator>() {
+ @Override protected Transliterator initialValue() {
+ return new Transliterator("el-Upper");
+ }
+ };
+
public static String toUpperCase(Locale locale, String s, char[] value, int offset, int count) {
String languageCode = locale.getLanguage();
if (languageCode.equals("tr") || languageCode.equals("az") || languageCode.equals("lt")) {
return ICU.toUpperCase(s, locale.toString());
}
+ if (languageCode.equals("el")) {
+ return EL_UPPER.get().transliterate(s);
+ }
char[] output = null;
int i = 0;
diff --git a/luni/src/main/java/java/lang/Integer.java b/luni/src/main/java/java/lang/Integer.java
index fc38b41..8ae0312 100644
--- a/luni/src/main/java/java/lang/Integer.java
+++ b/luni/src/main/java/java/lang/Integer.java
@@ -126,7 +126,8 @@
/**
* Compares two {@code int} values.
- * @return 0 if lhs = rhs, less than 0 if lhs < rhs, and greater than 0 if lhs > rhs.
+ * @return 0 if lhs = rhs, less than 0 if lhs < rhs, and greater than 0
+ * if lhs > rhs.
* @since 1.7
*/
public static int compare(int lhs, int rhs) {
@@ -140,24 +141,26 @@
/**
* Parses the specified string and returns a {@code Integer} instance if the
* string can be decoded into an integer value. The string may be an
- * optional minus sign "-" followed by a hexadecimal ("0x..." or "#..."),
- * octal ("0..."), or decimal ("...") representation of an integer.
+ * optional sign character ("-" or "+") followed by a hexadecimal ("0x..."
+ * or "#..."), octal ("0..."), or decimal ("...") representation of an
+ * integer.
*
* @param string
* a string representation of an integer value.
* @return an {@code Integer} containing the value represented by
* {@code string}.
* @throws NumberFormatException
- * if {@code string} cannot be parsed as an integer value.
+ * if {@code string} cannot be parsed as an integer value.
*/
public static Integer decode(String string) throws NumberFormatException {
- int length = string.length(), i = 0;
+ int length = string.length();
if (length == 0) {
throw invalidInt(string);
}
+ int i = 0;
char firstDigit = string.charAt(i);
boolean negative = firstDigit == '-';
- if (negative) {
+ if (negative || firstDigit == '+') {
if (length == 1) {
throw invalidInt(string);
}
@@ -319,8 +322,8 @@
/**
* Parses the specified string as a signed decimal integer value. The ASCII
- * character \u002d ('-') is recognized as the minus sign.
- *
+ * characters \u002d ('-') and \u002b ('+') are recognized as the minus and
+ * plus signs.
* @param string
* the string representation of an integer value.
* @return the primitive integer value represented by {@code string}.
@@ -333,7 +336,8 @@
/**
* Parses the specified string as a signed integer value using the specified
- * radix. The ASCII character \u002d ('-') is recognized as the minus sign.
+ * radix. The ASCII characters \u002d ('-') and \u002b ('+') are recognized
+ * as the minus and plus signs.
*
* @param string
* the string representation of an integer value.
@@ -350,24 +354,56 @@
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
throw new NumberFormatException("Invalid radix: " + radix);
}
- if (string == null) {
- throw invalidInt(string);
- }
- int length = string.length(), i = 0;
- if (length == 0) {
- throw invalidInt(string);
- }
- boolean negative = string.charAt(i) == '-';
- if (negative && ++i == length) {
+ if (string == null || string.isEmpty()) {
throw invalidInt(string);
}
- return parse(string, i, radix, negative);
+ char firstChar = string.charAt(0);
+ int firstDigitIndex = (firstChar == '-' || firstChar == '+') ? 1 : 0;
+ if (firstDigitIndex == string.length()) {
+ throw invalidInt(string);
+ }
+
+ return parse(string, firstDigitIndex, radix, firstChar == '-');
+ }
+
+ /**
+ * Equivalent to {@code parsePositiveInt(string, 10)}.
+ *
+ * @see #parsePositiveInt(String, int)
+ *
+ * @hide
+ */
+ public static int parsePositiveInt(String string) throws NumberFormatException {
+ return parsePositiveInt(string, 10);
+ }
+
+ /**
+ * Parses the specified string as a positive integer value using the
+ * specified radix. 0 is considered a positive integer.
+ * <p>
+ * This method behaves the same as {@link #parseInt(String, int)} except
+ * that it disallows leading '+' and '-' characters. See that method for
+ * error conditions.
+ *
+ * @see #parseInt(String, int)
+ *
+ * @hide
+ */
+ public static int parsePositiveInt(String string, int radix) throws NumberFormatException {
+ if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+ throw new NumberFormatException("Invalid radix: " + radix);
+ }
+ if (string == null || string.length() == 0) {
+ throw invalidInt(string);
+ }
+ return parse(string, 0, radix, false);
}
private static int parse(String string, int offset, int radix, boolean negative) throws NumberFormatException {
int max = Integer.MIN_VALUE / radix;
- int result = 0, length = string.length();
+ int result = 0;
+ int length = string.length();
while (offset < length) {
int digit = Character.digit(string.charAt(offset++), radix);
if (digit == -1) {
diff --git a/luni/src/main/java/java/lang/Long.java b/luni/src/main/java/java/lang/Long.java
index 84169af..5c11564 100644
--- a/luni/src/main/java/java/lang/Long.java
+++ b/luni/src/main/java/java/lang/Long.java
@@ -127,8 +127,8 @@
/**
* Parses the specified string and returns a {@code Long} instance if the
* string can be decoded into a long value. The string may be an optional
- * minus sign "-" followed by a hexadecimal ("0x..." or "#..."), octal
- * ("0..."), or decimal ("...") representation of a long.
+ * optional sign character ("-" or "+") followed by a hexadecimal ("0x..."
+ * or "#..."), octal ("0..."), or decimal ("...") representation of a long.
*
* @param string
* a string representation of a long value.
@@ -137,13 +137,15 @@
* if {@code string} cannot be parsed as a long value.
*/
public static Long decode(String string) throws NumberFormatException {
- int length = string.length(), i = 0;
+ int length = string.length();
if (length == 0) {
throw invalidLong(string);
}
+
+ int i = 0;
char firstDigit = string.charAt(i);
boolean negative = firstDigit == '-';
- if (negative) {
+ if (negative || firstDigit == '+') {
if (length == 1) {
throw invalidLong(string);
}
@@ -306,7 +308,8 @@
/**
* Parses the specified string as a signed decimal long value. The ASCII
- * character \u002d ('-') is recognized as the minus sign.
+ * characters \u002d ('-') and \u002b ('+') are recognized as the minus and
+ * plus signs.
*
* @param string
* the string representation of a long value.
@@ -320,7 +323,8 @@
/**
* Parses the specified string as a signed long value using the specified
- * radix. The ASCII character \u002d ('-') is recognized as the minus sign.
+ * radix. The ASCII characters \u002d ('-') and \u002b ('+') are recognized
+ * as the minus and plus signs.
*
* @param string
* the string representation of a long value.
@@ -337,24 +341,22 @@
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
throw new NumberFormatException("Invalid radix: " + radix);
}
- if (string == null) {
+ if (string == null || string.isEmpty()) {
throw invalidLong(string);
}
- int length = string.length(), i = 0;
- if (length == 0) {
- throw invalidLong(string);
- }
- boolean negative = string.charAt(i) == '-';
- if (negative && ++i == length) {
+ char firstChar = string.charAt(0);
+ int firstDigitIndex = (firstChar == '-' || firstChar == '+') ? 1 : 0;
+ if (firstDigitIndex == string.length()) {
throw invalidLong(string);
}
- return parse(string, i, radix, negative);
+ return parse(string, firstDigitIndex, radix, firstChar == '-');
}
private static long parse(String string, int offset, int radix, boolean negative) {
long max = Long.MIN_VALUE / radix;
- long result = 0, length = string.length();
+ long result = 0;
+ int length = string.length();
while (offset < length) {
int digit = Character.digit(string.charAt(offset++), radix);
if (digit == -1) {
@@ -378,6 +380,39 @@
return result;
}
+ /**
+ * Equivalent to {@code parsePositiveLong(string, 10)}.
+ *
+ * @see #parsePositiveLong(String, int)
+ *
+ * @hide
+ */
+ public static long parsePositiveLong(String string) throws NumberFormatException {
+ return parsePositiveLong(string, 10);
+ }
+
+ /**
+ * Parses the specified string as a positive long value using the
+ * specified radix. 0 is considered a positive long.
+ * <p>
+ * This method behaves the same as {@link #parseLong(String, int)} except
+ * that it disallows leading '+' and '-' characters. See that method for
+ * error conditions.
+ *
+ * @see #parseLong(String, int)
+ *
+ * @hide
+ */
+ public static long parsePositiveLong(String string, int radix) throws NumberFormatException {
+ if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+ throw new NumberFormatException("Invalid radix: " + radix);
+ }
+ if (string == null || string.length() == 0) {
+ throw invalidLong(string);
+ }
+ return parse(string, 0, radix, false);
+ }
+
@Override
public short shortValue() {
return (short) value;
diff --git a/luni/src/main/java/java/lang/ProcessManager.java b/luni/src/main/java/java/lang/ProcessManager.java
index 28314b7..ec87fda 100644
--- a/luni/src/main/java/java/lang/ProcessManager.java
+++ b/luni/src/main/java/java/lang/ProcessManager.java
@@ -16,23 +16,23 @@
package java.lang;
+import android.system.ErrnoException;
+import android.util.MutableInt;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.InputStream;
+import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import libcore.util.MutableInt;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* Manages child processes.
diff --git a/luni/src/main/java/java/lang/Runtime.java b/luni/src/main/java/java/lang/Runtime.java
index 8538f8a..4b66c2d 100644
--- a/luni/src/main/java/java/lang/Runtime.java
+++ b/luni/src/main/java/java/lang/Runtime.java
@@ -48,7 +48,7 @@
import libcore.io.IoUtils;
import libcore.io.Libcore;
import libcore.util.EmptyArray;
-import static libcore.io.OsConstants._SC_NPROCESSORS_CONF;
+import static android.system.OsConstants._SC_NPROCESSORS_CONF;
/**
* Allows Java applications to interface with the environment in which they are
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index e219fea..ea87929 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -32,6 +32,9 @@
package java.lang;
+import android.system.ErrnoException;
+import android.system.StructPasswd;
+import android.system.StructUtsname;
import dalvik.system.VMRuntime;
import dalvik.system.VMStack;
import java.io.BufferedInputStream;
@@ -39,8 +42,8 @@
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.InputStream;
+import java.io.IOException;
import java.io.PrintStream;
import java.nio.channels.Channel;
import java.nio.channels.spi.SelectorProvider;
@@ -51,10 +54,7 @@
import java.util.Properties;
import java.util.Set;
import libcore.icu.ICU;
-import libcore.io.ErrnoException;
import libcore.io.Libcore;
-import libcore.io.StructPasswd;
-import libcore.io.StructUtsname;
/**
* Provides access to system-related information and resources including
@@ -85,6 +85,22 @@
private static final Properties unchangeableSystemProperties;
private static Properties systemProperties;
+ /**
+ * Dedicated lock for GC / Finalization logic.
+ */
+ private static final Object lock = new Object();
+
+ /**
+ * Whether or not we need to do a GC before running the finalizers.
+ */
+ private static boolean runGC;
+
+ /**
+ * If we just ran finalization, we might want to do a GC to free the finalized objects.
+ * This lets us do gc/runFinlization/gc sequences but prevents back to back System.gc().
+ */
+ private static boolean justRanFinalization;
+
static {
err = new PrintStream(new FileOutputStream(FileDescriptor.err));
out = new PrintStream(new FileOutputStream(FileDescriptor.out));
@@ -251,7 +267,18 @@
* that the garbage collector will actually be run.
*/
public static void gc() {
- Runtime.getRuntime().gc();
+ boolean shouldRunGC;
+ synchronized(lock) {
+ shouldRunGC = justRanFinalization;
+ if (shouldRunGC) {
+ justRanFinalization = false;
+ } else {
+ runGC = true;
+ }
+ }
+ if (shouldRunGC) {
+ Runtime.getRuntime().gc();
+ }
}
/**
@@ -637,7 +664,18 @@
* to perform any outstanding object finalization.
*/
public static void runFinalization() {
+ boolean shouldRunGC;
+ synchronized(lock) {
+ shouldRunGC = runGC;
+ runGC = false;
+ }
+ if (shouldRunGC) {
+ Runtime.getRuntime().gc();
+ }
Runtime.getRuntime().runFinalization();
+ synchronized(lock) {
+ justRanFinalization = true;
+ }
}
/**
diff --git a/luni/src/main/java/java/lang/reflect/Array.java b/luni/src/main/java/java/lang/reflect/Array.java
index 088a434..a7dacfe 100644
--- a/luni/src/main/java/java/lang/reflect/Array.java
+++ b/luni/src/main/java/java/lang/reflect/Array.java
@@ -352,16 +352,16 @@
public static Object newInstance(Class<?> componentType, int size) throws NegativeArraySizeException {
if (!componentType.isPrimitive()) {
return createObjectArray(componentType, size);
- } else if (componentType == boolean.class) {
- return new boolean[size];
- } else if (componentType == byte.class) {
- return new byte[size];
} else if (componentType == char.class) {
return new char[size];
- } else if (componentType == short.class) {
- return new short[size];
} else if (componentType == int.class) {
return new int[size];
+ } else if (componentType == byte.class) {
+ return new byte[size];
+ } else if (componentType == boolean.class) {
+ return new boolean[size];
+ } else if (componentType == short.class) {
+ return new short[size];
} else if (componentType == long.class) {
return new long[size];
} else if (componentType == float.class) {
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index 49f141a..f9b72d8 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -17,14 +17,14 @@
package java.net;
+import android.system.ErrnoException;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.DatagramChannel;
-import libcore.io.ErrnoException;
import libcore.io.IoBridge;
import libcore.io.Libcore;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* This class implements a UDP socket for sending and receiving {@code
@@ -114,6 +114,17 @@
}
/**
+ * Sets the DatagramSocket and its related DatagramSocketImpl state as if a successful close()
+ * took place, without actually performing an OS close().
+ *
+ * @hide used in java.nio
+ */
+ public void onClose() {
+ isClosed = true;
+ impl.onClose();
+ }
+
+ /**
* Disconnects this UDP datagram socket from the remote host. This method
* called on an unconnected socket does nothing.
*/
@@ -127,6 +138,19 @@
isConnected = false;
}
+ /**
+ * Sets the DatagramSocket and its related DatagramSocketImpl state as if a successful
+ * disconnect() took place, without actually performing a disconnect().
+ *
+ * @hide used in java.nio
+ */
+ public void onDisconnect() {
+ address = null;
+ port = -1;
+ isConnected = false;
+ impl.onDisconnect();
+ }
+
synchronized void createSocket(int aPort, InetAddress addr) throws SocketException {
impl = factory != null ? factory.createDatagramSocketImpl()
: new PlainDatagramSocketImpl();
@@ -152,8 +176,8 @@
}
/**
- * Returns the local address to which this socket is bound,
- * or {@code null} if this socket is closed.
+ * Returns the local address to which this socket is bound, a wildcard address if this
+ * socket is not yet bound, or {@code null} if this socket is closed.
*/
public InetAddress getLocalAddress() {
try {
@@ -439,9 +463,12 @@
*/
public void bind(SocketAddress localAddr) throws SocketException {
checkOpen();
- int localPort = 0;
- InetAddress addr = Inet4Address.ANY;
- if (localAddr != null) {
+ int localPort;
+ InetAddress addr;
+ if (localAddr == null) {
+ localPort = 0;
+ addr = Inet4Address.ANY;
+ } else {
if (!(localAddr instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
localAddr.getClass());
@@ -459,6 +486,17 @@
}
/**
+ * Sets the DatagramSocket and its related DatagramSocketImpl state as if a successful bind()
+ * took place, without actually performing an OS bind().
+ *
+ * @hide used in java.nio
+ */
+ public void onBind(InetAddress localAddress, int localPort) {
+ isBound = true;
+ impl.onBind(localAddress, localPort);
+ }
+
+ /**
* Connects this datagram socket to the address and port specified by {@code peer}.
* Future calls to {@link #send} will use this as the default target, and {@link #receive}
* will only accept packets from this source.
@@ -492,6 +530,19 @@
}
/**
+ * Sets the DatagramSocket and its related DatagramSocketImpl state as if a successful connect()
+ * took place, without actually performing an OS connect().
+ *
+ * @hide used in java.nio
+ */
+ public void onConnect(InetAddress remoteAddress, int remotePort) {
+ isConnected = true;
+ this.address = remoteAddress;
+ this.port = remotePort;
+ impl.onConnect(remoteAddress, remotePort);
+ }
+
+ /**
* Connects this datagram socket to the specific {@code address} and {@code port}.
* Future calls to {@link #send} will use this as the default target, and {@link #receive}
* will only accept packets from this source.
@@ -537,10 +588,11 @@
}
/**
- * Returns the {@code SocketAddress} this socket is bound to, or null for an unbound socket.
+ * Returns the {@code SocketAddress} this socket is bound to, or {@code null} for an unbound or
+ * closed socket.
*/
public SocketAddress getLocalSocketAddress() {
- if (!isBound()) {
+ if (isClosed() || !isBound()) {
return null;
}
return new InetSocketAddress(getLocalAddress(), getLocalPort());
diff --git a/luni/src/main/java/java/net/DatagramSocketImpl.java b/luni/src/main/java/java/net/DatagramSocketImpl.java
index 097eb17..1a39987 100644
--- a/luni/src/main/java/java/net/DatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/DatagramSocketImpl.java
@@ -268,4 +268,40 @@
* if an error occurs while peeking at the data.
*/
protected abstract int peekData(DatagramPacket pack) throws IOException;
+
+ /**
+ * Initialize the bind() state.
+ * @hide used in java.nio.
+ */
+ protected void onBind(InetAddress localAddress, int localPort) {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the connect() state.
+ * @hide used in java.nio.
+ */
+ protected void onConnect(InetAddress remoteAddress, int remotePort) {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the disconnected state.
+ * @hide used in java.nio.
+ */
+ protected void onDisconnect() {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the closed state.
+ * @hide used in java.nio.
+ */
+ protected void onClose() {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
}
diff --git a/luni/src/main/java/java/net/HttpCookie.java b/luni/src/main/java/java/net/HttpCookie.java
index 0fe6db0..04121f7 100644
--- a/luni/src/main/java/java/net/HttpCookie.java
+++ b/luni/src/main/java/java/net/HttpCookie.java
@@ -53,10 +53,12 @@
* in this format is {@code 1}.
* </ul>
*
- * <p>This implementation silently discards unrecognized attributes. In
- * particular, the {@code HttpOnly} attribute is widely served but isn't in any
- * of the above specs. It was introduced by Internet Explorer to prevent server
- * cookies from being exposed in the DOM to JavaScript, etc.
+ * <p>Support for the "HttpOnly" attribute specified in
+ * <a href="http://tools.ietf.org/html/rfc6265">RFC 6265</a> is also included. RFC 6265 is intended
+ * to obsolete RFC 2965. Support for features from RFC 2965 that have been deprecated by RFC 6265
+ * such as Cookie2, Set-Cookie2 headers and version information remain supported by this class.
+ *
+ * <p>This implementation silently discards unrecognized attributes.
*
* @since 1.6
*/
@@ -65,16 +67,17 @@
private static final Set<String> RESERVED_NAMES = new HashSet<String>();
static {
- RESERVED_NAMES.add("comment"); // RFC 2109 RFC 2965
- RESERVED_NAMES.add("commenturl"); // RFC 2965
- RESERVED_NAMES.add("discard"); // RFC 2965
- RESERVED_NAMES.add("domain"); // Netscape RFC 2109 RFC 2965
+ RESERVED_NAMES.add("comment"); // RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("commenturl"); // RFC 2965 RFC 6265
+ RESERVED_NAMES.add("discard"); // RFC 2965 RFC 6265
+ RESERVED_NAMES.add("domain"); // Netscape RFC 2109 RFC 2965 RFC 6265
RESERVED_NAMES.add("expires"); // Netscape
- RESERVED_NAMES.add("max-age"); // RFC 2109 RFC 2965
- RESERVED_NAMES.add("path"); // Netscape RFC 2109 RFC 2965
- RESERVED_NAMES.add("port"); // RFC 2965
- RESERVED_NAMES.add("secure"); // Netscape RFC 2109 RFC 2965
- RESERVED_NAMES.add("version"); // RFC 2109 RFC 2965
+ RESERVED_NAMES.add("httponly"); // RFC 6265
+ RESERVED_NAMES.add("max-age"); // RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("path"); // Netscape RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("port"); // RFC 2965 RFC 6265
+ RESERVED_NAMES.add("secure"); // Netscape RFC 2109 RFC 2965 RFC 6265
+ RESERVED_NAMES.add("version"); // RFC 2109 RFC 2965 RFC 6265
}
/**
@@ -350,6 +353,8 @@
cookie.portList = value != null ? value : "";
} else if (name.equals("secure")) {
cookie.secure = true;
+ } else if (name.equals("httponly")) {
+ cookie.httpOnly = true;
} else if (name.equals("version") && !hasVersion) {
cookie.version = Integer.parseInt(value);
}
@@ -440,6 +445,7 @@
private String path;
private String portList;
private boolean secure;
+ private boolean httpOnly;
private String value;
private int version = 1;
@@ -554,6 +560,28 @@
}
/**
+ * Returns the {@code HttpOnly} attribute. If {@code true} the cookie should not be accessible
+ * to scripts in a browser.
+ *
+ * @since 1.7
+ * @hide Until ready for an API update
+ */
+ public boolean isHttpOnly() {
+ return httpOnly;
+ }
+
+ /**
+ * Returns the {@code HttpOnly} attribute. If {@code true} the cookie should not be accessible
+ * to scripts in a browser.
+ *
+ * @since 1.7
+ * @hide Until ready for an API update
+ */
+ public void setHttpOnly(boolean httpOnly) {
+ this.httpOnly = httpOnly;
+ }
+
+ /**
* Returns the value of this cookie.
*/
public String getValue() {
@@ -708,7 +736,21 @@
/**
* Returns a string representing this cookie in the format used by the
- * {@code Cookie} header line in an HTTP request.
+ * {@code Cookie} header line in an HTTP request as specified by RFC 2965 section 3.3.4.
+ *
+ * <p>The resulting string does not include a "Cookie:" prefix or any version information.
+ * The returned {@code String} is not suitable for passing to {@link #parse(String)}: Several of
+ * the attributes that would be needed to preserve all of the cookie's information are omitted.
+ * The String is formatted for an HTTP request not an HTTP response.
+ *
+ * <p>The attributes included and the format depends on the cookie's {@code version}:
+ * <ul>
+ * <li>Version 0: Includes only the name and value. Conforms to RFC 2965 (for
+ * version 0 cookies). This should also be used to conform with RFC 6265.
+ * </li>
+ * <li>Version 1: Includes the name and value, and Path, Domain and Port attributes.
+ * Conforms to RFC 2965 (for version 1 cookies).</li>
+ * </ul>
*/
@Override public String toString() {
if (version == 0) {
diff --git a/luni/src/main/java/java/net/HttpURLConnection.java b/luni/src/main/java/java/net/HttpURLConnection.java
index 89a4bc4..4e5b4ee 100644
--- a/luni/src/main/java/java/net/HttpURLConnection.java
+++ b/luni/src/main/java/java/net/HttpURLConnection.java
@@ -784,11 +784,15 @@
* only servers may not support this mode.
*
* <p>When HTTP chunked encoding is used, the stream is divided into
- * chunks, each prefixed with a header containing the chunk's size. Setting
- * a large chunk length requires a large internal buffer, potentially
- * wasting memory. Setting a small chunk length increases the number of
+ * chunks, each prefixed with a header containing the chunk's size.
+ * A large chunk length requires a large internal buffer, potentially
+ * wasting memory. A small chunk length increases the number of
* bytes that must be transmitted because of the header on every chunk.
- * Most caller should use {@code 0} to get the system default.
+ *
+ * <p>Implementation details: In some releases the {@code chunkLength} is
+ * treated as a hint: chunks sent to the server may actually be larger or
+ * smaller. To force a chunk to be sent to the server call
+ * {@link java.io.OutputStream#flush()}.
*
* @see #setFixedLengthStreamingMode
* @param chunkLength the length to use, or {@code 0} for the default chunk
diff --git a/luni/src/main/java/java/net/Inet4Address.java b/luni/src/main/java/java/net/Inet4Address.java
index 7c26639..f0b1b5b 100644
--- a/luni/src/main/java/java/net/Inet4Address.java
+++ b/luni/src/main/java/java/net/Inet4Address.java
@@ -20,7 +20,7 @@
import java.io.ObjectStreamException;
import java.nio.ByteOrder;
import libcore.io.Memory;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* An IPv4 address. See {@link InetAddress}.
diff --git a/luni/src/main/java/java/net/Inet6Address.java b/luni/src/main/java/java/net/Inet6Address.java
index 37e9c18..8ab0f8d 100644
--- a/luni/src/main/java/java/net/Inet6Address.java
+++ b/luni/src/main/java/java/net/Inet6Address.java
@@ -23,7 +23,7 @@
import java.io.ObjectStreamField;
import java.util.Arrays;
import java.util.Enumeration;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* An IPv6 address. See {@link InetAddress}.
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index 98ad098..e31b4c3 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -17,6 +17,9 @@
package java.net;
+import android.system.ErrnoException;
+import android.system.GaiException;
+import android.system.StructAddrinfo;
import dalvik.system.BlockGuard;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -28,18 +31,13 @@
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
-import libcore.io.ErrnoException;
-import libcore.io.GaiException;
+import java.util.concurrent.CountDownLatch;
+import java.util.List;
import libcore.io.IoBridge;
import libcore.io.Libcore;
import libcore.io.Memory;
-import libcore.io.StructAddrinfo;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* An Internet Protocol (IP) address. This can be either an IPv4 address or an IPv6 address, and
@@ -734,7 +732,7 @@
}
}
- IoBridge.closeSocket(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
return reached;
}
diff --git a/luni/src/main/java/java/net/InetUnixAddress.java b/luni/src/main/java/java/net/InetUnixAddress.java
index 44b9cba..51236e2 100644
--- a/luni/src/main/java/java/net/InetUnixAddress.java
+++ b/luni/src/main/java/java/net/InetUnixAddress.java
@@ -18,7 +18,7 @@
import java.nio.charset.StandardCharsets;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* An AF_UNIX address. See {@link InetAddress}.
diff --git a/luni/src/main/java/java/net/MulticastSocket.java b/luni/src/main/java/java/net/MulticastSocket.java
index 6f4a582..24e66c5 100644
--- a/luni/src/main/java/java/net/MulticastSocket.java
+++ b/luni/src/main/java/java/net/MulticastSocket.java
@@ -229,6 +229,9 @@
private void checkJoinOrLeave(InetAddress groupAddr) throws IOException {
checkOpen();
+ if (groupAddr == null) {
+ throw new IllegalArgumentException("groupAddress == null");
+ }
if (!groupAddr.isMulticastAddress()) {
throw new IOException("Not a multicast group: " + groupAddr);
}
@@ -351,7 +354,8 @@
/**
* Disables multicast loopback if {@code disable == true}.
* See {@link SocketOptions#IP_MULTICAST_LOOP}, and note that the sense of this is the
- * opposite of the underlying Unix {@code IP_MULTICAST_LOOP}.
+ * opposite of the underlying Unix {@code IP_MULTICAST_LOOP}: true means disabled, false
+ * means enabled.
*
* @throws SocketException if an error occurs.
*/
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java
index 3128b98..852c09b 100644
--- a/luni/src/main/java/java/net/NetworkInterface.java
+++ b/luni/src/main/java/java/net/NetworkInterface.java
@@ -17,6 +17,7 @@
package java.net;
+import android.system.ErrnoException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -26,10 +27,9 @@
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* This class is used to represent a network interface of the local device. An
diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
index 3226527..eb0c99d 100644
--- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -17,23 +17,15 @@
package java.net;
+import android.system.ErrnoException;
+import android.system.StructGroupReq;
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocketImpl;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import libcore.io.ErrnoException;
import libcore.io.IoBridge;
import libcore.io.Libcore;
-import libcore.io.StructGroupReq;
import libcore.util.EmptyArray;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* @hide used in java.nio.
@@ -78,15 +70,25 @@
}
@Override
+ protected void onBind(InetAddress localAddress, int localPort) {
+ this.localPort = localPort;
+ }
+
+ @Override
public synchronized void close() {
guard.close();
try {
- IoBridge.closeSocket(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
} catch (IOException ignored) {
}
}
@Override
+ protected void onClose() {
+ guard.close();
+ }
+
+ @Override
public void create() throws SocketException {
this.fd = IoBridge.socket(false);
}
@@ -179,7 +181,8 @@
public void send(DatagramPacket packet) throws IOException {
int port = isNativeConnected ? 0 : packet.getPort();
InetAddress address = isNativeConnected ? null : packet.getAddress();
- IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address, port);
+ IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address,
+ port);
}
public void setOption(int option, Object value) throws SocketException {
@@ -211,6 +214,13 @@
}
@Override
+ protected void onConnect(InetAddress remoteAddress, int remotePort) {
+ isNativeConnected = true;
+ connectedAddress = remoteAddress;
+ connectedPort = remotePort;
+ }
+
+ @Override
public void disconnect() {
try {
Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
@@ -224,6 +234,13 @@
isNativeConnected = false;
}
+ @Override
+ protected void onDisconnect() {
+ connectedPort = -1;
+ connectedAddress = null;
+ isNativeConnected = false;
+ }
+
/**
* Set the received address and port in the packet. We do this when the
* Datagram socket is connected at the native level and the
diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java
index 18942d6..4e5ba44 100644
--- a/luni/src/main/java/java/net/PlainSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainSocketImpl.java
@@ -17,28 +17,19 @@
package java.net;
+import android.system.ErrnoException;
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.InputStream;
+import java.io.IOException;
import java.io.OutputStream;
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.SocketImpl;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
import java.nio.ByteOrder;
import java.util.Arrays;
-import libcore.io.ErrnoException;
import libcore.io.IoBridge;
import libcore.io.Libcore;
import libcore.io.Memory;
import libcore.io.Streams;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* @hide used in java.nio.
@@ -120,15 +111,6 @@
return proxy != null && proxy.type() == Proxy.Type.SOCKS;
}
- public void initLocalPort(int localPort) {
- this.localport = localPort;
- }
-
- public void initRemoteAddressAndPort(InetAddress remoteAddress, int remotePort) {
- this.address = remoteAddress;
- this.port = remotePort;
- }
-
private void checkNotClosed() throws IOException {
if (!fd.valid()) {
throw new SocketException("Socket is closed");
@@ -148,7 +130,6 @@
@Override protected void bind(InetAddress address, int port) throws IOException {
IoBridge.bind(fd, address, port);
- this.address = address;
if (port != 0) {
this.localport = port;
} else {
@@ -157,9 +138,19 @@
}
@Override
+ public void onBind(InetAddress localAddress, int localPort) {
+ localport = localPort;
+ }
+
+ @Override
protected synchronized void close() throws IOException {
guard.close();
- IoBridge.closeSocket(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ }
+
+ @Override
+ public void onClose() {
+ guard.close();
}
@Override
@@ -191,8 +182,14 @@
} else {
IoBridge.connect(fd, normalAddr, aPort, timeout);
}
- super.address = normalAddr;
- super.port = aPort;
+ address = normalAddr;
+ port = aPort;
+ }
+
+ @Override
+ public void onConnect(InetAddress remoteAddress, int remotePort) {
+ address = remoteAddress;
+ port = remotePort;
}
@Override
diff --git a/luni/src/main/java/java/net/ServerSocket.java b/luni/src/main/java/java/net/ServerSocket.java
index 399511f..72b197f 100644
--- a/luni/src/main/java/java/net/ServerSocket.java
+++ b/luni/src/main/java/java/net/ServerSocket.java
@@ -21,6 +21,8 @@
import java.io.IOException;
import java.nio.channels.ServerSocketChannel;
+import libcore.io.IoBridge;
+
/**
* This class represents a server-side socket that waits for incoming client
* connections. A {@code ServerSocket} handles the requests and sends back an
@@ -49,6 +51,8 @@
private boolean isClosed;
+ private InetAddress localAddress;
+
/**
* Constructs a new unbound {@code ServerSocket}.
*
@@ -99,7 +103,7 @@
impl.create(true);
try {
impl.bind(addr, port);
- isBound = true;
+ readBackBindState();
impl.listen(backlog > 0 ? backlog : DEFAULT_BACKLOG);
} catch (IOException e) {
close();
@@ -109,6 +113,14 @@
}
/**
+ * Read the cached isBound and localAddress state from the underlying OS socket.
+ */
+ private void readBackBindState() throws SocketException {
+ localAddress = IoBridge.getSocketLocalAddress(impl.fd);
+ isBound = true;
+ }
+
+ /**
* Waits for an incoming request and blocks until the connection is opened.
* This method returns a socket object representing the just opened
* connection.
@@ -152,8 +164,8 @@
}
/**
- * Gets the local IP address of this server socket or {@code null} if the
- * socket is unbound. This is useful for multihomed hosts.
+ * Gets the local IP address of this server socket if this socket has ever been bound,
+ * {@code null} otherwise. This is useful for multihomed hosts.
*
* @return the local address of this server socket.
*/
@@ -161,12 +173,13 @@
if (!isBound()) {
return null;
}
- return impl.getInetAddress();
+ return localAddress;
}
/**
- * Gets the local port of this server socket or {@code -1} if the socket is
- * unbound.
+ * Gets the local port of this server socket or {@code -1} if the socket is not bound.
+ * If the socket has ever been bound this method will return the local port it was bound to,
+ * even after it has been closed.
*
* @return the local port this server is listening on.
*/
@@ -300,9 +313,12 @@
if (isBound()) {
throw new BindException("Socket is already bound");
}
- int port = 0;
- InetAddress addr = Inet4Address.ANY;
- if (localAddr != null) {
+ InetAddress addr;
+ int port;
+ if (localAddr == null) {
+ addr = Inet4Address.ANY;
+ port = 0;
+ } else {
if (!(localAddr instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
localAddr.getClass());
@@ -317,7 +333,7 @@
synchronized (this) {
try {
impl.bind(addr, port);
- isBound = true;
+ readBackBindState();
impl.listen(backlog > 0 ? backlog : DEFAULT_BACKLOG);
} catch (IOException e) {
close();
@@ -327,8 +343,9 @@
}
/**
- * Gets the local socket address of this server socket or {@code null} if
- * the socket is unbound. This is useful on multihomed hosts.
+ * Gets the local socket address of this server socket or {@code null} if the socket is unbound.
+ * This is useful on multihomed hosts. If the socket has ever been bound this method will return
+ * the local address it was bound to, even after it has been closed.
*
* @return the local socket address and port this socket is bound to.
*/
@@ -336,7 +353,7 @@
if (!isBound()) {
return null;
}
- return new InetSocketAddress(getInetAddress(), getLocalPort());
+ return new InetSocketAddress(localAddress, getLocalPort());
}
/**
diff --git a/luni/src/main/java/java/net/Socket.java b/luni/src/main/java/java/net/Socket.java
index 36fdf28..5dd350a 100644
--- a/luni/src/main/java/java/net/Socket.java
+++ b/luni/src/main/java/java/net/Socket.java
@@ -312,12 +312,29 @@
*/
public synchronized void close() throws IOException {
isClosed = true;
- // RI compatibility: the RI returns the any address (but the original local port) after close.
+ isConnected = false;
+ // RI compatibility: the RI returns the any address (but the original local port) after
+ // close.
localAddress = Inet4Address.ANY;
impl.close();
}
/**
+ * Sets the Socket and its related SocketImpl state as if a successful close() took place,
+ * without actually performing an OS close().
+ *
+ * @hide used in java.nio
+ */
+ public void onClose() {
+ isClosed = true;
+ isConnected = false;
+ // RI compatibility: the RI returns the any address (but the original local port) after
+ // close.
+ localAddress = Inet4Address.ANY;
+ impl.onClose();
+ }
+
+ /**
* Returns the IP address of the target host this socket is connected to, or null if this
* socket is not yet connected.
*/
@@ -329,7 +346,9 @@
}
/**
- * Returns an input stream to read data from this socket.
+ * Returns an input stream to read data from this socket. If the socket has an associated
+ * {@link SocketChannel} and that channel is in non-blocking mode then reads from the
+ * stream will throw a {@link java.nio.channels.IllegalBlockingModeException}.
*
* @return the byte-oriented input stream.
* @throws IOException
@@ -353,15 +372,16 @@
}
/**
- * Returns the local IP address this socket is bound to, or {@code InetAddress.ANY} if
- * the socket is unbound.
+ * Returns the local IP address this socket is bound to, or an address for which
+ * {@link InetAddress#isAnyLocalAddress()} returns true if the socket is closed or unbound.
*/
public InetAddress getLocalAddress() {
return localAddress;
}
/**
- * Returns the local port this socket is bound to, or -1 if the socket is unbound.
+ * Returns the local port this socket is bound to, or -1 if the socket is unbound. If the socket
+ * has been closed this method will still return the local port the socket was bound to.
*/
public int getLocalPort() {
if (!isBound()) {
@@ -371,7 +391,9 @@
}
/**
- * Returns an output stream to write data into this socket.
+ * Returns an output stream to write data into this socket. If the socket has an associated
+ * {@link SocketChannel} and that channel is in non-blocking mode then writes to the
+ * stream will throw a {@link java.nio.channels.IllegalBlockingModeException}.
*
* @return the byte-oriented output stream.
* @throws IOException
@@ -564,6 +586,7 @@
impl.bind(addr, localPort);
}
isBound = true;
+ cacheLocalAddress();
impl.connect(dstAddress, dstPort);
isConnected = true;
cacheLocalAddress();
@@ -672,9 +695,10 @@
}
/**
- * Returns the local address and port of this socket as a SocketAddress or
- * null if the socket is unbound. This is useful on multihomed
- * hosts.
+ * Returns the local address and port of this socket as a SocketAddress or null if the socket
+ * has never been bound. If the socket is closed but has previously been bound then an address
+ * for which {@link InetAddress#isAnyLocalAddress()} returns true will be returned with the
+ * previously-bound port. This is useful on multihomed hosts.
*/
public SocketAddress getLocalSocketAddress() {
if (!isBound()) {
@@ -744,9 +768,12 @@
throw new BindException("Socket is already bound");
}
- int port = 0;
- InetAddress addr = Inet4Address.ANY;
- if (localAddr != null) {
+ int port;
+ InetAddress addr;
+ if (localAddr == null) {
+ port = 0;
+ addr = Inet4Address.ANY;
+ } else {
if (!(localAddr instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
localAddr.getClass());
@@ -771,6 +798,18 @@
}
/**
+ * Sets the Socket and its related SocketImpl state as if a successful bind() took place,
+ * without actually performing an OS bind().
+ *
+ * @hide used in java.nio
+ */
+ public void onBind(InetAddress localAddress, int localPort) {
+ isBound = true;
+ this.localAddress = localAddress;
+ impl.onBind(localAddress, localPort);
+ }
+
+ /**
* Connects this socket to the given remote host address and port specified
* by the SocketAddress {@code remoteAddr}.
*
@@ -851,6 +890,17 @@
}
/**
+ * Sets the Socket and its related SocketImpl state as if a successful connect() took place,
+ * without actually performing an OS connect().
+ *
+ * @hide internal use only
+ */
+ public void onConnect(InetAddress remoteAddress, int remotePort) {
+ isConnected = true;
+ impl.onConnect(remoteAddress, remotePort);
+ }
+
+ /**
* Returns whether the incoming channel of the socket has already been
* closed.
*
diff --git a/luni/src/main/java/java/net/SocketImpl.java b/luni/src/main/java/java/net/SocketImpl.java
index 92de9cf..bd36ec7 100644
--- a/luni/src/main/java/java/net/SocketImpl.java
+++ b/luni/src/main/java/java/net/SocketImpl.java
@@ -294,4 +294,31 @@
*/
protected void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
}
+
+ /**
+ * Initialize the bind() state.
+ * @hide used in java.nio.
+ */
+ public void onBind(InetAddress localAddress, int localPort) {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the connect() state.
+ * @hide used in java.nio.
+ */
+ public void onConnect(InetAddress remoteAddress, int remotePort) {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the close() state.
+ * @hide used in java.nio.
+ */
+ public void onClose() {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
}
diff --git a/luni/src/main/java/java/net/SocketOption.java b/luni/src/main/java/java/net/SocketOption.java
new file mode 100644
index 0000000..3f65494
--- /dev/null
+++ b/luni/src/main/java/java/net/SocketOption.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 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;
+
+/**
+ * An option associated with a socket.
+ *
+ * <p>See {@link java.nio.channels.NetworkChannel#setOption},
+ * {@link java.nio.channels.NetworkChannel#getOption} and
+ * {@link java.nio.channels.NetworkChannel#supportedOptions} for methods that use SocketOption.
+ *
+ * <p>See {@link StandardSocketOptions} for valid SocketOptions.
+ *
+ * @param <T> the type of the value
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+public interface SocketOption<T> {
+
+ /**
+ * Returns the name of the option.
+ */
+ String name();
+
+ /**
+ * Returns the type of the value of the option.
+ */
+ Class<T> type();
+}
diff --git a/luni/src/main/java/java/net/SocketOptions.java b/luni/src/main/java/java/net/SocketOptions.java
index e23fc97..d0df689 100644
--- a/luni/src/main/java/java/net/SocketOptions.java
+++ b/luni/src/main/java/java/net/SocketOptions.java
@@ -28,19 +28,27 @@
*/
public interface SocketOptions {
/**
- * Number of seconds to wait when closing a socket if there
- * is still some buffered data to be sent.
+ * Number of seconds to wait when closing a socket if there is still some buffered data to be
+ * sent.
*
- * <p>If this option is set to 0, the TCP socket is closed forcefully and the
- * call to {@code close} returns immediately.
+ * <p>The option can be set to disabled using {@link #setOption(int, Object)} with a value of
+ * {@code Boolean.FALSE}.
*
- * <p>If this option is set to a value greater than 0, the value is interpreted
- * as the number of seconds to wait. If all data could be sent
- * during this time, the socket is closed normally. Otherwise the connection will be
- * closed forcefully.
+ * <p>If this option is set to 0, the TCP socket is closed forcefully and the call to
+ * {@code close} returns immediately.
*
- * <p>Valid values for this option are in the range 0 to 65535 inclusive. (Larger
+ * If this option is disabled, closing a socket will return immediately and the close will be
+ * handled in the background.
+ *
+ * <p>If this option is set to a value greater than 0, the value is interpreted as the number of
+ * seconds to wait. If all data could be sent during this time, the socket is closed normally.
+ * Otherwise the connection will be closed forcefully.
+ *
+ * <p>Valid numeric values for this option are in the range 0 to 65535 inclusive. (Larger
* timeouts will be treated as 65535s timeouts; roughly 18 hours.)
+ *
+ * <p>This option is intended for use with sockets in blocking mode. The behavior of this option
+ * for non-blocking sockets is undefined.
*/
public static final int SO_LINGER = 128;
@@ -54,16 +62,21 @@
public static final int SO_TIMEOUT = 4102;
/**
- * This boolean option specifies whether data is sent immediately on this socket.
- * As a side-effect this could lead to low packet efficiency. The
- * socket implementation uses the Nagle's algorithm to try to reach a higher
- * packet efficiency if this option is disabled.
+ * This boolean option specifies whether data is sent immediately on this socket or buffered.
+ * <p>
+ * If set to {@code Boolean.TRUE} the Nagle algorithm is disabled and there is no buffering.
+ * This could lead to low packet efficiency. When set to {@code Boolean.FALSE} the the socket
+ * implementation uses buffering to try to reach a higher packet efficiency.
+ *
+ * <p>See <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122: Requirements for Internet
+ * Hosts -- Communication Layers</a> for more information about buffering and the Nagle
+ * algorithm.
*/
public static final int TCP_NODELAY = 1;
/**
* This is an IPv4-only socket option whose functionality is subsumed by
- * {@link #IP_MULTICAST_IF} and not implemented on Android.
+ * {@link #IP_MULTICAST_IF2} and not implemented on Android.
*/
public static final int IP_MULTICAST_IF = 16;
@@ -73,9 +86,18 @@
public static final int SO_BINDADDR = 15;
/**
- * This boolean option specifies whether a reuse of a local address is allowed even
- * if another socket is not yet removed by the operating system. It's only
- * available on a {@code MulticastSocket}.
+ * This boolean option specifies whether a reuse of a local address is allowed when another
+ * socket has not yet been removed by the operating system.
+ *
+ * <p>For connection-oriented sockets, if this option is disabled and if there is another socket
+ * in state TIME_WAIT on a given address then another socket binding to that address would fail.
+ * Setting this value after a socket is bound has no effect.
+ *
+ * <p>For datagram sockets this option determines whether several sockets can listen on the
+ * same address; when enabled each socket will receive a copy of the datagram.
+ *
+ * <p>See <a href="https://www.ietf.org/rfc/rfc793.txt">RFC 793: Transmission Control Protocol
+ * </a> for more information about socket re-use.
*/
public static final int SO_REUSEADDR = 4;
@@ -93,11 +115,18 @@
* This is a hint to the kernel; the kernel may use a larger buffer.
*
* <p>For datagram sockets, packets larger than this value will be discarded.
+ *
+ * <p>See <a href="http://www.ietf.org/rfc/rfc1323.txt">RFC1323: TCP Extensions for High
+ * Performance</a> for more information about TCP/IP buffering.
*/
public static final int SO_RCVBUF = 4098;
/**
- * This boolean option specifies whether the kernel sends keepalive messages.
+ * This boolean option specifies whether the kernel sends keepalive messages on
+ * connection-oriented sockets.
+ *
+ * <p>See <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122: Requirements for Internet
+ * Hosts -- Communication Layers</a> for more information on keep-alive.
*/
public static final int SO_KEEPALIVE = 8;
@@ -114,15 +143,18 @@
/**
* This boolean option specifies whether the local loopback of multicast packets is
- * enabled or disabled. This option is enabled by default on multicast
- * sockets. Note that the sense of this option in Java is the
- * <i>opposite</i> of the underlying Unix {@code IP_MULTICAST_LOOP}.
- * See {@link MulticastSocket#setLoopbackMode}.
+ * enabled or disabled. This loopback is enabled by default on multicast sockets.
+ *
+ * <p>See <a href="http://tools.ietf.org/rfc/rfc1112.txt">RFC 1112: Host Extensions for IP
+ * Multicasting</a> for more information about IP multicast.
+ *
+ * <p>See {@link MulticastSocket#setLoopbackMode}.
*/
public static final int IP_MULTICAST_LOOP = 18;
/**
- * This boolean option can be used to enable broadcasting on datagram sockets.
+ * This boolean option can be used to enable or disable broadcasting on datagram sockets. This
+ * option must be enabled to send broadcast messages. The default value is false.
*/
public static final int SO_BROADCAST = 32;
@@ -135,6 +167,9 @@
/**
* This integer option sets the outgoing interface for multicast packets
* using an interface index.
+ *
+ * <p>See <a href="http://tools.ietf.org/rfc/rfc1112.txt">RFC 1112: Host Extensions for IP
+ * Multicasting</a> for more information about IP multicast.
*/
public static final int IP_MULTICAST_IF2 = 31;
diff --git a/luni/src/main/java/java/net/StandardSocketOptions.java b/luni/src/main/java/java/net/StandardSocketOptions.java
new file mode 100644
index 0000000..3d10caf
--- /dev/null
+++ b/luni/src/main/java/java/net/StandardSocketOptions.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2014 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.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import libcore.io.IoBridge;
+
+/**
+ * Defines the set standard of socket options that can be supported by network channels.
+ *
+ * <p>See {@link java.nio.channels.NetworkChannel} for more information, particularly
+ * {@link java.nio.channels.NetworkChannel#supportedOptions()} for the options that are supported
+ * for each type of socket.
+ *
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+public final class StandardSocketOptions {
+
+ /**
+ * The outgoing interface for multicast packets.
+ *
+ * <p>See {@link SocketOptions#IP_MULTICAST_IF2} for further documentation.
+ */
+ public static final SocketOption<NetworkInterface> IP_MULTICAST_IF =
+ new NetworkInterfaceSocketOption("IP_MULTICAST_IF", SocketOptions.IP_MULTICAST_IF2);
+
+ /**
+ * Whether the local loopback of multicast packets is enabled (true) or disabled (false). This
+ * option is enabled by default.
+ *
+ * <p>See {@link SocketOptions#IP_MULTICAST_LOOP} for further documentation.
+ */
+ public static final SocketOption<Boolean> IP_MULTICAST_LOOP =
+ new BooleanSocketOption("IP_MULTICAST_LOOP", SocketOptions.IP_MULTICAST_LOOP);
+
+ /**
+ * The time-to-live (TTL) for multicast packets. The value must be between 0 and 255 inclusive.
+ * A 0 value restricts the packet to the originating host. See also {@link #IP_MULTICAST_LOOP}.
+ * The default value is 1.
+ *
+ * <p>See <a href="http://tools.ietf.org/rfc/rfc1112.txt">RFC 1112: Host Extensions for IP
+ * Multicasting</a> for more information about IP multicast.
+ */
+ public static final SocketOption<Integer> IP_MULTICAST_TTL =
+ new ByteRangeSocketOption("IP_MULTICAST_TTL", IoBridge.JAVA_IP_MULTICAST_TTL);
+
+ /**
+ * The value for the type-of-service field of the IPv4 header, or the traffic class field of the
+ * IPv6 header. These correspond to the IP_TOS and IPV6_TCLASS socket options. These may be
+ * ignored by the underlying OS. Values must be between 0 and 255 inclusive.
+ *
+ * <p>See {@link SocketOptions#IP_TOS} for further documentation.
+ */
+ public static final SocketOption<Integer> IP_TOS =
+ new ByteRangeSocketOption("IP_TOS", SocketOptions.IP_TOS);
+
+ /**
+ * Whether broadcasting on datagram sockets is enabled or disabled. This option must be enabled to
+ * send broadcast messages. The default value is false.
+ *
+ * <p>See {@link SocketOptions#SO_BROADCAST} for further documentation.
+ */
+ public static final SocketOption<Boolean> SO_BROADCAST =
+ new BooleanSocketOption("SO_BROADCAST", SocketOptions.SO_BROADCAST);
+
+ /**
+ * Whether the kernel sends keepalive messages on connection-oriented sockets.
+ *
+ * <p>See {@link SocketOptions#SO_KEEPALIVE} for further documentation.
+ */
+ public static final SocketOption<Boolean> SO_KEEPALIVE =
+ new BooleanSocketOption("SO_KEEPALIVE", SocketOptions.SO_KEEPALIVE);
+
+ /**
+ * Number of seconds to wait when closing a socket if there is still some buffered data to be
+ * sent.
+ *
+ * <p>If this option is negative this option is disabled. This is the default value. If the value
+ * is 0 or positive it is enabled.
+ *
+ * <p>See {@link SocketOptions#SO_LINGER} for further documentation.
+ *
+ */
+ public static final SocketOption<Integer> SO_LINGER =
+ new SocketOptionImpl<Integer>("SO_LINGER", Integer.class, SocketOptions.SO_LINGER) {
+ @Override
+ protected Object validateAndConvertValueBeforeSet(
+ FileDescriptor fd, Integer value) {
+ Object objectValue = super.validateAndConvertValueBeforeSet(fd, value);
+ if (value != null && value < 0) {
+ // IoBridge requires a "false" object to disable linger.
+ objectValue = Boolean.FALSE;
+ }
+ return objectValue;
+ }
+
+ @Override
+ protected Integer validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ // IoBridge returns a "false" object to indicate that linger is disabled.
+ if (value != null && value instanceof Boolean) {
+ value = -1;
+ }
+ return super.validateAndConvertValueAfterGet(fd, value);
+ }
+ };
+
+ /**
+ * The size in bytes of a socket's receive buffer. This must be an integer greater than 0.
+ * This is a hint to the kernel; the kernel may use a larger buffer.
+ *
+ * <p>See {@link SocketOptions#SO_RCVBUF} for further documentation.
+ */
+ public static final SocketOption<Integer> SO_RCVBUF =
+ new PositiveIntegerSocketOption("SO_RCVBUF", SocketOptions.SO_RCVBUF);
+
+ /**
+ * Whether a reuse of a local address is allowed when another socket has not yet been removed by
+ * the operating system.
+ *
+ * <p>See {@link SocketOptions#SO_REUSEADDR} for further documentation.
+ */
+ public static final SocketOption<Boolean> SO_REUSEADDR =
+ new BooleanSocketOption("SO_REUSEADDR", SocketOptions.SO_REUSEADDR);
+
+ /**
+ * The size in bytes of a socket's send buffer. This must be an integer greater than 0.
+ * This is a hint to the kernel; the kernel may use a larger buffer.
+ *
+ * <p>See {@link SocketOptions#SO_SNDBUF} for further documentation.
+ */
+ public static final SocketOption<Integer> SO_SNDBUF =
+ new PositiveIntegerSocketOption("SO_SNDBUF", SocketOptions.SO_SNDBUF);
+
+ /**
+ * Specifies whether data is sent immediately on this socket or buffered.
+ *
+ * <p>See {@link SocketOptions#TCP_NODELAY} for further documentation.
+ */
+ public static final SocketOption<Boolean> TCP_NODELAY =
+ new BooleanSocketOption("TCP_NODELAY", SocketOptions.TCP_NODELAY);
+
+ /**
+ * The set of supported options for UDP sockets.
+ *
+ * @hide internal use only
+ */
+ public static final Set<SocketOption<?>> DATAGRAM_SOCKET_OPTIONS;
+
+ static {
+ HashSet<SocketOption<?>> mutableSet = new HashSet<SocketOption<?>>(8);
+ mutableSet.add(IP_MULTICAST_IF);
+ mutableSet.add(IP_MULTICAST_LOOP);
+ mutableSet.add(IP_MULTICAST_TTL);
+ mutableSet.add(IP_TOS);
+ mutableSet.add(SO_BROADCAST);
+ mutableSet.add(SO_REUSEADDR);
+ mutableSet.add(SO_RCVBUF);
+ mutableSet.add(SO_SNDBUF);
+ DATAGRAM_SOCKET_OPTIONS = Collections.unmodifiableSet(mutableSet);
+ }
+
+ /**
+ * The set of supported options for TCP sockets.
+ *
+ * @hide internal use only
+ */
+ public static final Set<SocketOption<?>> SOCKET_OPTIONS;
+
+ static {
+ HashSet<SocketOption<?>> mutableSet = new HashSet<SocketOption<?>>(7);
+ mutableSet.add(IP_TOS);
+ mutableSet.add(SO_KEEPALIVE);
+ mutableSet.add(SO_LINGER);
+ mutableSet.add(TCP_NODELAY);
+ mutableSet.add(SO_RCVBUF);
+ mutableSet.add(SO_REUSEADDR);
+ mutableSet.add(SO_SNDBUF);
+ SOCKET_OPTIONS = Collections.unmodifiableSet(mutableSet);
+ }
+
+ /**
+ * The set of supported options for TCP server sockets.
+ *
+ * @hide internal use only
+ */
+ public static final Set<SocketOption<?>> SERVER_SOCKET_OPTIONS;
+
+ static {
+ HashSet<SocketOption<?>> mutableSet = new HashSet<SocketOption<?>>(2);
+ mutableSet.add(SO_RCVBUF);
+ mutableSet.add(SO_REUSEADDR);
+ SERVER_SOCKET_OPTIONS = Collections.unmodifiableSet(mutableSet);
+ }
+
+ /**
+ * A base class for SocketOption objects that passes the values to/from {@link IoBridge} as they
+ * are. For use with simple types like Integer and Boolean, and can be extended for more
+ * validation / type conversion.
+ *
+ * @hide internal use only
+ */
+ public static class SocketOptionImpl<T> implements SocketOption<T> {
+
+ protected final String name;
+
+ private final Class<T> type;
+
+ protected final int socketOption;
+
+ public SocketOptionImpl(String name, Class<T> type, int socketOption) {
+ this.name = name;
+ this.type = type;
+ this.socketOption = socketOption;
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public Class<T> type() {
+ return type;
+ }
+
+ /**
+ * Sets the socket option of the file descriptor to value using IoBridge.
+ *
+ * @hide internal method
+ */
+ public final void setValue(FileDescriptor fd, T value) throws IOException {
+ // Sanity check required because of type erasure.
+ if (value != null && !type.isAssignableFrom(value.getClass())) {
+ throw new AssertionError("Invalid type " + value + " of value for " + name);
+ }
+ Object objectValue = validateAndConvertValueBeforeSet(fd, value);
+ IoBridge.setSocketOption(fd, socketOption, objectValue);
+ }
+
+ /**
+ * Throws IllegalArgumentException if the value is outside of the acceptable range.
+ * Subclasses can override to apply option-specific validate, and may also convert the value
+ * to a different type or value. The default implementation prevents null values and returns
+ * the value unchanged.
+ */
+ protected Object validateAndConvertValueBeforeSet(FileDescriptor fd, T value) {
+ if (value == null) {
+ throw new IllegalArgumentException("value for " + name + " must not be null");
+ }
+ return value;
+ }
+
+ /**
+ * Gets the value of the socket option.
+ *
+ * @hide internal method
+ */
+ public final T getValue(FileDescriptor fd) throws IOException {
+ Object value = IoBridge.getSocketOption(fd, socketOption);
+ T typedValue = validateAndConvertValueAfterGet(fd, value);
+ if (typedValue != null && !type.isAssignableFrom(typedValue.getClass())) {
+ // Sanity check required because of type erasure.
+ throw new AssertionError("Unexpected type of value returned for " + name);
+ }
+ return typedValue;
+ }
+
+ /**
+ * Throws AssertionError if the value is outside of the acceptable range.
+ * Implementations may also convert the value to a different type or
+ * value. The default implementation does nothing.
+ */
+ @SuppressWarnings("unchecked")
+ protected T validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ return (T) value;
+ }
+ }
+
+ /**
+ * A SocketOption capable of setting / getting an boolean value.
+ */
+ private static class BooleanSocketOption extends SocketOptionImpl<Boolean> {
+
+ public BooleanSocketOption(String name, int socketOption) {
+ super(name, Boolean.class, socketOption);
+ }
+ }
+
+ /**
+ * A SocketOption capable of setting / getting an network interface value.
+ */
+ private static class NetworkInterfaceSocketOption extends SocketOptionImpl<NetworkInterface> {
+
+ public NetworkInterfaceSocketOption(String name, int socketOption) {
+ super(name, NetworkInterface.class, socketOption);
+ }
+
+ @Override
+ public Integer validateAndConvertValueBeforeSet(FileDescriptor fd, NetworkInterface value) {
+ if (value == null) {
+ throw new IllegalArgumentException("value for " + name + " must not be null");
+ }
+ int nicIndex = value.getIndex();
+ if (nicIndex == -1) {
+ throw new IllegalArgumentException("The NetworkInterface must have a valid index");
+ }
+ return nicIndex;
+ }
+
+ @Override
+ public NetworkInterface validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ if (value == null) {
+ return null;
+ } else if (!(value instanceof Integer)) {
+ throw new AssertionError("Unexpected type of value returned for " + name);
+ }
+
+ int nicIndex = (Integer) value;
+ try {
+ return NetworkInterface.getByIndex(nicIndex);
+ } catch (SocketException e) {
+ throw new IllegalArgumentException(
+ "Unable to resolve NetworkInterface index: " + nicIndex, e);
+ }
+ }
+ }
+
+ /**
+ * A SocketOption capable of setting / getting an integer in the range 0-255.
+ */
+ private static class ByteRangeSocketOption extends SocketOptionImpl<Integer> {
+
+ public ByteRangeSocketOption(String name, int socketOption) {
+ super(name, Integer.class, socketOption);
+ }
+
+ @Override
+ protected Object validateAndConvertValueBeforeSet(FileDescriptor fd, Integer value) {
+ if (value == null || value < 0 || value > 255) {
+ throw new IllegalArgumentException(name + " must be >= 0 and <= 255, is " + value);
+ }
+ return value;
+ }
+
+ @Override
+ protected Integer validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ if (!(value instanceof Integer)) {
+ throw new AssertionError("Unexpected value for option " + name + ": " + value);
+ }
+ int intValue = (Integer) value;
+ if (intValue < 0 || intValue > 255) {
+ throw new AssertionError("Unexpected value for option " + name + ": " + value);
+ }
+ return intValue;
+ }
+ }
+
+ /**
+ * A SocketOption capable of setting / getting an integer in the range 1..
+ */
+ private static class PositiveIntegerSocketOption extends SocketOptionImpl<Integer> {
+
+ public PositiveIntegerSocketOption(String name, int socketOption) {
+ super(name, Integer.class, socketOption);
+ }
+
+ @Override
+ protected Integer validateAndConvertValueBeforeSet(FileDescriptor fd, Integer value) {
+ if (value < 1) {
+ throw new IllegalArgumentException(name + " value must be > 0");
+ }
+ return value;
+ }
+
+ @Override
+ protected Integer validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ if (!(value instanceof Integer)) {
+ throw new AssertionError("Unexpected value for option " + name + ": " + value);
+ }
+ int intValue = (Integer) value;
+ if (intValue < 1) {
+ throw new AssertionError("Unexpected value for option " + name + ": " + value);
+ }
+ return intValue;
+ }
+ }
+}
diff --git a/luni/src/main/java/java/net/URI.java b/luni/src/main/java/java/net/URI.java
index 09268b8..f206473 100644
--- a/luni/src/main/java/java/net/URI.java
+++ b/luni/src/main/java/java/net/URI.java
@@ -461,11 +461,14 @@
if (index < (temp.length() - 1)) { // port part is not empty
try {
- tempPort = Integer.parseInt(temp.substring(index + 1));
- if (tempPort < 0) {
+ char firstPortChar = temp.charAt(index + 1);
+ if (firstPortChar >= '0' && firstPortChar <= '9') {
+ // allow only digits, no signs
+ tempPort = Integer.parseInt(temp.substring(index + 1));
+ } else {
if (forceServer) {
throw new URISyntaxException(authority,
- "Invalid port number", hostIndex + index + 1);
+ "Invalid port number", hostIndex + index + 1);
}
return;
}
diff --git a/luni/src/main/java/java/net/URLConnection.java b/luni/src/main/java/java/net/URLConnection.java
index 74c15ce..cc7de90 100644
--- a/luni/src/main/java/java/net/URLConnection.java
+++ b/luni/src/main/java/java/net/URLConnection.java
@@ -308,15 +308,25 @@
/**
* Returns the content length in bytes specified by the response header field
- * {@code content-length} or {@code -1} if this field is not set.
- *
- * @return the value of the response header field {@code content-length}.
+ * {@code content-length} or {@code -1} if this field is not set or cannot be represented as an
+ * {@code int}.
*/
public int getContentLength() {
return getHeaderFieldInt("Content-Length", -1);
}
/**
+ * Returns the content length in bytes specified by the response header field
+ * {@code content-length} or {@code -1} if this field is not set.
+ *
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+ public long getContentLengthLong() {
+ return getHeaderFieldLong("Content-Length", -1);
+ }
+
+ /**
* Returns the MIME-type of the content specified by the response header field
* {@code content-type} or {@code null} if type is unknown.
*
@@ -531,7 +541,7 @@
/**
* Returns the specified header value as a number. Returns the {@code
* defaultValue} if no such header field could be found or the value could
- * not be parsed as an {@code Integer}.
+ * not be parsed as an {@code int}.
*
* @param field
* the header field name whose value is needed.
@@ -548,6 +558,27 @@
}
/**
+ * Returns the specified header value as a number. Returns the {@code
+ * defaultValue} if no such header field could be found or the value could
+ * not be parsed as a {@code long}.
+ *
+ * @param field
+ * the header field name whose value is needed.
+ * @param defaultValue
+ * the default value if no field has been found.
+ * @return the value of the specified header field as a number.
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+ public long getHeaderFieldLong(String field, long defaultValue) {
+ try {
+ return Long.parseLong(getHeaderField(field));
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ /**
* Returns the name of the header field at the given position {@code posn} or
* {@code null} if there are fewer than {@code posn} fields. The base
* implementation of this method returns always {@code null}.
diff --git a/luni/src/main/java/java/net/URLStreamHandler.java b/luni/src/main/java/java/net/URLStreamHandler.java
index 8a6c264..d21bb9c 100644
--- a/luni/src/main/java/java/net/URLStreamHandler.java
+++ b/luni/src/main/java/java/net/URLStreamHandler.java
@@ -131,9 +131,11 @@
host = spec.substring(hostStart, hostEnd);
int portStart = hostEnd + 1;
if (portStart < fileStart) {
- port = Integer.parseInt(spec.substring(portStart, fileStart));
- if (port < 0) {
- throw new IllegalArgumentException("port < 0: " + port);
+ char firstPortChar = spec.charAt(portStart);
+ if (firstPortChar >= '0' && firstPortChar <= '9') {
+ port = Integer.parseInt(spec.substring(portStart, fileStart));
+ } else {
+ throw new IllegalArgumentException("invalid port: " + port);
}
}
path = null;
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 39f2128..8a5dbb6 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -17,26 +17,35 @@
package java.nio;
+import android.system.ErrnoException;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.InterruptedIOException;
+import java.io.IOException;
import java.net.ConnectException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.DatagramSocketImpl;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
import java.net.PlainDatagramSocketImpl;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
+import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.IllegalBlockingModeException;
+import java.nio.channels.MembershipKey;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.spi.SelectorProvider;
+import java.nio.channels.UnresolvedAddressException;
+import java.nio.channels.UnsupportedAddressTypeException;
import java.util.Arrays;
-import libcore.io.ErrnoException;
+import java.util.Set;
import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Libcore;
@@ -50,10 +59,13 @@
private final FileDescriptor fd;
// Our internal DatagramSocket.
- private DatagramSocket socket = null;
+ private DatagramSocket socket;
- // The address to be connected.
- InetSocketAddress connectAddress = null;
+ // The remote address to be connected.
+ InetSocketAddress connectAddress;
+
+ // The local address.
+ InetAddress localAddress;
// local port
private int localPort;
@@ -67,6 +79,9 @@
private final Object readLock = new Object();
private final Object writeLock = new Object();
+ // A helper to manage multicast group membership. Created as required.
+ private MulticastMembershipHandler multicastMembershipHandler;
+
/*
* Constructor
*/
@@ -97,20 +112,91 @@
return socket;
}
+ /** @hide Until ready for a public API change */
+ @Override
+ synchronized public DatagramChannel bind(SocketAddress local) throws IOException {
+ checkOpen();
+ if (isBound) {
+ throw new AlreadyBoundException();
+ }
+
+ if (local == null) {
+ local = new InetSocketAddress(Inet4Address.ANY, 0);
+ } else if (!(local instanceof InetSocketAddress)) {
+ throw new UnsupportedAddressTypeException();
+ }
+
+ InetSocketAddress localAddress = (InetSocketAddress) local;
+ if (localAddress.isUnresolved()) {
+ throw new UnresolvedAddressException();
+ }
+ IoBridge.bind(fd, localAddress.getAddress(), localAddress.getPort());
+ onBind(true /* updateSocketState */);
+ return this;
+ }
+
/**
- * @see java.nio.channels.DatagramChannel#isConnected()
+ * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when
+ * some or all of the bound state has been left to the OS to decide, or when the Socket handled
+ * bind() or connect().
+ *
+ * @param updateSocketState
+ * if the associated socket (if present) needs to be updated
+ * @hide used to sync state, non-private to avoid synthetic method
*/
+ void onBind(boolean updateSocketState) {
+ SocketAddress sa;
+ try {
+ sa = Libcore.os.getsockname(fd);
+ } catch (ErrnoException errnoException) {
+ throw new AssertionError(errnoException);
+ }
+ isBound = true;
+ InetSocketAddress localSocketAddress = (InetSocketAddress) sa;
+ localAddress = localSocketAddress.getAddress();
+ localPort = localSocketAddress.getPort();
+ if (updateSocketState && socket != null) {
+ socket.onBind(localAddress, localPort);
+ }
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ synchronized public SocketAddress getLocalAddress() throws IOException {
+ checkOpen();
+ return isBound ? new InetSocketAddress(localAddress, localPort) : null;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ return NioUtils.getSocketOption(
+ this, StandardSocketOptions.DATAGRAM_SOCKET_OPTIONS, option);
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> DatagramChannel setOption(SocketOption<T> option, T value) throws IOException {
+ checkOpen();
+ NioUtils.setSocketOption(
+ this, StandardSocketOptions.DATAGRAM_SOCKET_OPTIONS, option, value);
+ return this;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return StandardSocketOptions.DATAGRAM_SOCKET_OPTIONS;
+ }
+
@Override
synchronized public boolean isConnected() {
return connected;
}
- /**
- * @see java.nio.channels.DatagramChannel#connect(java.net.SocketAddress)
- */
@Override
synchronized public DatagramChannel connect(SocketAddress address) throws IOException {
- // must open
+ // must be open
checkOpen();
// status must be un-connected.
if (connected) {
@@ -119,43 +205,71 @@
// check the address
InetSocketAddress inetSocketAddress = SocketChannelImpl.validateAddress(address);
+ InetAddress remoteAddress = inetSocketAddress.getAddress();
+ int remotePort = inetSocketAddress.getPort();
try {
begin();
- IoBridge.connect(fd, inetSocketAddress.getAddress(), inetSocketAddress.getPort());
+ IoBridge.connect(fd, remoteAddress, remotePort);
} catch (ConnectException e) {
// ConnectException means connect fail, not exception
} finally {
end(true);
}
- // set the connected address.
- connectAddress = inetSocketAddress;
- connected = true;
- isBound = true;
+ // connect() performs a bind() if an explicit bind() was not performed. Keep the local
+ // address state held by the channel and the socket up to date.
+ if (!isBound) {
+ onBind(true /* updateSocketState */);
+ }
+
+ // Keep the connected state held by the channel and the socket up to date.
+ onConnect(remoteAddress, remotePort, true /* updateSocketState */);
return this;
}
/**
- * @see java.nio.channels.DatagramChannel#disconnect()
+ * Initialize the state associated with being connected, optionally syncing the socket if there
+ * is one.
+ * @hide used to sync state, non-private to avoid synthetic method
*/
+ void onConnect(InetAddress remoteAddress, int remotePort, boolean updateSocketState) {
+ connected = true;
+ connectAddress = new InetSocketAddress(remoteAddress, remotePort);
+ if (updateSocketState && socket != null) {
+ socket.onConnect(remoteAddress, remotePort);
+ }
+ }
+
@Override
synchronized public DatagramChannel disconnect() throws IOException {
if (!isConnected() || !isOpen()) {
return this;
}
- connected = false;
- connectAddress = null;
+
+ // Keep the disconnected state held by the channel and the socket up to date.
+ onDisconnect(true /* updateSocketState */);
+
try {
Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsIOException();
}
- if (socket != null) {
- socket.disconnect();
- }
return this;
}
+ /**
+ * Initialize the state associated with being disconnected, optionally syncing the socket if
+ * there is one.
+ * @hide used to sync state, non-private to avoid synthetic method
+ */
+ void onDisconnect(boolean updateSocketState) {
+ connected = false;
+ connectAddress = null;
+ if (updateSocketState && socket != null && socket.isConnected()) {
+ socket.onDisconnect();
+ }
+ }
+
@Override
public SocketAddress receive(ByteBuffer target) throws IOException {
target.checkWritable();
@@ -191,7 +305,7 @@
SocketAddress retAddr = null;
DatagramPacket receivePacket;
int oldposition = target.position();
- int received = 0;
+ int received;
// TODO: disallow mapped buffers and lose this conditional?
if (target.hasArray()) {
receivePacket = new DatagramPacket(target.array(), target.position() + target.arrayOffset(), target.remaining());
@@ -200,7 +314,7 @@
}
do {
received = IoBridge.recvfrom(false, fd, receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength(), 0, receivePacket, isConnected());
- if (receivePacket != null && receivePacket.getAddress() != null) {
+ if (receivePacket.getAddress() != null) {
if (received > 0) {
if (target.hasArray()) {
target.position(oldposition + received);
@@ -220,10 +334,10 @@
SocketAddress retAddr = null;
DatagramPacket receivePacket = new DatagramPacket(EmptyArray.BYTE, 0);
int oldposition = target.position();
- int received = 0;
+ int received;
do {
received = IoBridge.recvfrom(false, fd, target, 0, receivePacket, isConnected());
- if (receivePacket != null && receivePacket.getAddress() != null) {
+ if (receivePacket.getAddress() != null) {
// copy the data of received packet
if (received > 0) {
target.position(oldposition + received);
@@ -259,7 +373,9 @@
if (sendCount > 0) {
source.position(oldPosition + sendCount);
}
- isBound = true;
+ if (!isBound) {
+ onBind(true /* updateSocketState */);
+ }
} finally {
end(sendCount >= 0);
}
@@ -276,7 +392,7 @@
return 0;
}
- int readCount = 0;
+ int readCount;
if (target.isDirect() || target.hasArray()) {
readCount = readImpl(target);
if (readCount > 0) {
@@ -405,11 +521,13 @@
}
@Override protected synchronized void implCloseSelectableChannel() throws IOException {
- connected = false;
+ // A closed channel is not connected.
+ onDisconnect(true /* updateSocketState */);
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ multicastMembershipHandler = null;
+
if (socket != null && !socket.isClosed()) {
- socket.close();
- } else {
- IoBridge.closeSocket(fd);
+ socket.onClose();
}
}
@@ -420,7 +538,7 @@
/*
* Status check, must be open.
*/
- private void checkOpen() throws IOException {
+ private void checkOpen() throws ClosedChannelException {
if (!isOpen()) {
throw new ClosedChannelException();
}
@@ -452,6 +570,52 @@
return fd;
}
+ @Override
+ synchronized public MembershipKey join(InetAddress groupAddress,
+ NetworkInterface networkInterface) throws IOException {
+
+ checkOpen();
+ ensureMembershipHandlerExists();
+ return multicastMembershipHandler.addAnySourceMembership(networkInterface, groupAddress);
+ }
+
+ @Override
+ synchronized public MembershipKey join(
+ InetAddress groupAddress, NetworkInterface networkInterface, InetAddress sourceAddress)
+ throws IOException {
+ checkOpen();
+ ensureMembershipHandlerExists();
+ return multicastMembershipHandler.addSourceSpecificMembership(
+ networkInterface, groupAddress, sourceAddress);
+ }
+
+ synchronized void multicastDrop(MembershipKeyImpl membershipKey) {
+ ensureMembershipHandlerExists();
+ multicastMembershipHandler.dropMembership(membershipKey);
+ }
+
+ synchronized void multicastBlock(MembershipKeyImpl membershipKey, InetAddress sourceAddress)
+ throws SocketException {
+
+ ensureMembershipHandlerExists();
+ multicastMembershipHandler.block(membershipKey, sourceAddress);
+ }
+
+ synchronized void multicastUnblock(MembershipKeyImpl membershipKey, InetAddress sourceAddress) {
+ ensureMembershipHandlerExists();
+ multicastMembershipHandler.unblock(membershipKey, sourceAddress);
+ }
+
+ /**
+ * Creates the {@code multicastMembershipHandler} if one doesn't already exist. Callers must
+ * handle synchronization.
+ */
+ private void ensureMembershipHandlerExists() {
+ if (multicastMembershipHandler == null) {
+ multicastMembershipHandler = new MulticastMembershipHandler(this);
+ }
+ }
+
/*
* The adapter class of DatagramSocket
*/
@@ -460,15 +624,29 @@
/*
* The internal datagramChannelImpl.
*/
- private DatagramChannelImpl channelImpl;
+ private final DatagramChannelImpl channelImpl;
/*
* Constructor initialize the datagramSocketImpl and datagramChannelImpl
*/
- DatagramSocketAdapter(DatagramSocketImpl socketimpl,
- DatagramChannelImpl channelImpl) {
+ DatagramSocketAdapter(DatagramSocketImpl socketimpl, DatagramChannelImpl channelImpl) {
super(socketimpl);
this.channelImpl = channelImpl;
+
+ // Sync state socket state with the channel it is being created from
+ if (channelImpl.isBound) {
+ onBind(channelImpl.localAddress, channelImpl.localPort);
+ }
+ if (channelImpl.connected) {
+ onConnect(
+ channelImpl.connectAddress.getAddress(),
+ channelImpl.connectAddress.getPort());
+ } else {
+ onDisconnect();
+ }
+ if (!channelImpl.isOpen()) {
+ onClose();
+ }
}
/*
@@ -479,92 +657,78 @@
return channelImpl;
}
- /**
- * @see java.net.DatagramSocket#isBound()
- */
- @Override
- public boolean isBound() {
- return channelImpl.isBound;
- }
-
- /**
- * @see java.net.DatagramSocket#isConnected()
- */
- @Override
- public boolean isConnected() {
- return channelImpl.isConnected();
- }
-
- /**
- * @see java.net.DatagramSocket#getInetAddress()
- */
- @Override
- public InetAddress getInetAddress() {
- if (channelImpl.connectAddress == null) {
- return null;
- }
- return channelImpl.connectAddress.getAddress();
- }
-
- @Override public InetAddress getLocalAddress() {
- try {
- return IoBridge.getSocketLocalAddress(channelImpl.fd);
- } catch (SocketException ex) {
- return null;
- }
- }
-
- /**
- * @see java.net.DatagramSocket#getPort()
- */
- @Override
- public int getPort() {
- if (channelImpl.connectAddress == null) {
- return -1;
- }
- return channelImpl.connectAddress.getPort();
- }
-
- /**
- * @see java.net.DatagramSocket#bind(java.net.SocketAddress)
- */
@Override
public void bind(SocketAddress localAddr) throws SocketException {
if (channelImpl.isConnected()) {
throw new AlreadyConnectedException();
}
super.bind(localAddr);
- channelImpl.isBound = true;
+ channelImpl.onBind(false /* updateSocketState */);
}
- /**
- * @see java.net.DatagramSocket#receive(java.net.DatagramPacket)
- */
+ @Override
+ public void connect(SocketAddress peer) throws SocketException {
+ if (isConnected()) {
+ // RI compatibility: If the socket is already connected this fails.
+ throw new IllegalStateException("Socket is already connected.");
+ }
+ super.connect(peer);
+ // Connect may have performed an implicit bind(). Sync up here.
+ channelImpl.onBind(false /* updateSocketState */);
+
+ InetSocketAddress inetSocketAddress = (InetSocketAddress) peer;
+ channelImpl.onConnect(
+ inetSocketAddress.getAddress(), inetSocketAddress.getPort(),
+ false /* updateSocketState */);
+ }
+
+ @Override
+ public void connect(InetAddress address, int port) {
+ // To avoid implementing connect() twice call this.connect(SocketAddress) in preference
+ // to super.connect().
+ try {
+ connect(new InetSocketAddress(address, port));
+ } catch (SocketException e) {
+ // Ignored - there is nothing we can report here.
+ }
+ }
+
@Override
public void receive(DatagramPacket packet) throws IOException {
if (!channelImpl.isBlocking()) {
throw new IllegalBlockingModeException();
}
+
+ boolean wasBound = isBound();
super.receive(packet);
+ if (!wasBound) {
+ // DatagramSocket.receive() will implicitly bind if it hasn't been done explicitly.
+ // Sync the channel state with the socket.
+ channelImpl.onBind(false /* updateSocketState */);
+ }
}
- /**
- * @see java.net.DatagramSocket#send(java.net.DatagramPacket)
- */
@Override
public void send(DatagramPacket packet) throws IOException {
if (!channelImpl.isBlocking()) {
throw new IllegalBlockingModeException();
}
+
+ // DatagramSocket.send() will implicitly bind if it hasn't been done explicitly. Force
+ // bind() here so that the channel state stays in sync with the socket.
+ boolean wasBound = isBound();
super.send(packet);
+ if (!wasBound) {
+ // DatagramSocket.send() will implicitly bind if it hasn't been done explicitly.
+ // Sync the channel state with the socket.
+ channelImpl.onBind(false /* updateSocketState */);
+ }
}
- /**
- * @see java.net.DatagramSocket#close()
- */
@Override
public void close() {
synchronized (channelImpl) {
+ super.close();
if (channelImpl.isOpen()) {
try {
channelImpl.close();
@@ -572,21 +736,13 @@
// Ignore
}
}
- super.close();
}
}
- /**
- * @see java.net.DatagramSocket#disconnect()
- */
@Override
public void disconnect() {
- try {
- channelImpl.disconnect();
- } catch (IOException e) {
- // Ignore
- }
super.disconnect();
+ channelImpl.onDisconnect(false /* updateSocketState */);
}
}
}
diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java
index 6206d1b..4ed7dba 100644
--- a/luni/src/main/java/java/nio/FileChannelImpl.java
+++ b/luni/src/main/java/java/nio/FileChannelImpl.java
@@ -17,6 +17,9 @@
package java.nio;
+import android.system.ErrnoException;
+import android.system.StructFlock;
+import android.util.MutableLong;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -32,11 +35,8 @@
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;
-import libcore.io.ErrnoException;
import libcore.io.Libcore;
-import libcore.io.StructFlock;
-import libcore.util.MutableLong;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* Our concrete implementation of the abstract FileChannel class.
@@ -50,7 +50,7 @@
}
};
- private final Object stream;
+ private final Closeable ioObject;
private final FileDescriptor fd;
private final int mode;
@@ -61,9 +61,9 @@
* Create a new file channel implementation class that wraps the given
* fd and operates in the specified mode.
*/
- public FileChannelImpl(Object stream, FileDescriptor fd, int mode) {
+ public FileChannelImpl(Closeable ioObject, FileDescriptor fd, int mode) {
this.fd = fd;
- this.stream = stream;
+ this.ioObject = ioObject;
this.mode = mode;
}
@@ -86,9 +86,7 @@
}
protected void implCloseChannel() throws IOException {
- if (stream instanceof Closeable) {
- ((Closeable) stream).close();
- }
+ ioObject.close();
}
private FileLock basicLock(long position, long size, boolean shared, boolean wait) throws IOException {
diff --git a/luni/src/main/java/java/nio/IoVec.java b/luni/src/main/java/java/nio/IoVec.java
index f14f4f2..e20709c 100644
--- a/luni/src/main/java/java/nio/IoVec.java
+++ b/luni/src/main/java/java/nio/IoVec.java
@@ -16,10 +16,10 @@
package java.nio;
+import android.system.ErrnoException;
import java.io.FileDescriptor;
import java.io.IOException;
import libcore.io.Libcore;
-import libcore.io.ErrnoException;
/**
* Used to implement java.nio read(ByteBuffer[])/write(ByteBuffer[]) operations as POSIX readv(2)
diff --git a/luni/src/main/java/java/nio/MappedByteBuffer.java b/luni/src/main/java/java/nio/MappedByteBuffer.java
index 5782457..ce19c0c 100644
--- a/luni/src/main/java/java/nio/MappedByteBuffer.java
+++ b/luni/src/main/java/java/nio/MappedByteBuffer.java
@@ -16,11 +16,11 @@
package java.nio;
+import android.system.ErrnoException;
import java.nio.channels.FileChannel.MapMode;
-import libcore.io.ErrnoException;
import libcore.io.Libcore;
-import static libcore.io.OsConstants.MS_SYNC;
-import static libcore.io.OsConstants._SC_PAGE_SIZE;
+import static android.system.OsConstants.MS_SYNC;
+import static android.system.OsConstants._SC_PAGE_SIZE;
/**
* {@code MappedByteBuffer} is a special kind of direct byte buffer which maps a
diff --git a/luni/src/main/java/java/nio/MembershipKeyImpl.java b/luni/src/main/java/java/nio/MembershipKeyImpl.java
new file mode 100644
index 0000000..3d7e957
--- /dev/null
+++ b/luni/src/main/java/java/nio/MembershipKeyImpl.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 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.nio;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.nio.channels.MembershipKey;
+import java.nio.channels.MulticastChannel;
+
+/**
+ * An implementation of {@link MembershipKey}.
+ *
+ * To keep this class simple and keep all mutation operations in one place and easily synchronized,
+ * most socket logic is held in {@link java.nio.DatagramChannelImpl}.
+ */
+final class MembershipKeyImpl extends MembershipKey {
+
+ private final DatagramChannelImpl channel;
+ private final InetAddress groupAddress;
+ private final NetworkInterface networkInterface;
+ private final InetAddress sourceAddress;
+ private volatile boolean isValid;
+
+ public MembershipKeyImpl(DatagramChannelImpl channel, NetworkInterface networkInterface,
+ InetAddress groupAddress, InetAddress sourceAddress) {
+
+ this.channel = channel;
+ this.networkInterface = networkInterface;
+ this.groupAddress = groupAddress;
+ this.sourceAddress = sourceAddress;
+ this.isValid = true;
+ }
+
+ @Override
+ public boolean isValid() {
+ // invalidate() is called if the key is dropped, but for simplicity it is not
+ // invalidated when the channel is closed. Therefore, the channel must also be checked to see
+ // if it is still open.
+ return isValid && channel.isOpen();
+ }
+
+ void invalidate() {
+ this.isValid = false;
+ }
+
+ @Override
+ public void drop() {
+ channel.multicastDrop(this);
+ }
+
+ @Override
+ public MembershipKey block(InetAddress source) throws IOException {
+ channel.multicastBlock(this, source);
+ return this;
+ }
+
+ @Override
+ synchronized public MembershipKey unblock(InetAddress source) {
+ channel.multicastUnblock(this, source);
+ return this;
+ }
+
+ @Override
+ public MulticastChannel channel() {
+ return channel;
+ }
+
+ @Override
+ public InetAddress group() {
+ return groupAddress;
+ }
+
+ @Override
+ public NetworkInterface networkInterface() {
+ return networkInterface;
+ }
+
+ @Override
+ public InetAddress sourceAddress() {
+ return sourceAddress;
+ }
+
+ @Override
+ public String toString() {
+ return "MembershipKeyImpl{" +
+ "groupAddress=" + groupAddress +
+ ", networkInterface=" + networkInterface +
+ ", sourceAddress=" + sourceAddress +
+ '}';
+ }
+}
diff --git a/luni/src/main/java/java/nio/MemoryBlock.java b/luni/src/main/java/java/nio/MemoryBlock.java
index 6052b8f..1ce8fea 100644
--- a/luni/src/main/java/java/nio/MemoryBlock.java
+++ b/luni/src/main/java/java/nio/MemoryBlock.java
@@ -17,18 +17,18 @@
package java.nio;
+import android.system.ErrnoException;
import dalvik.system.VMRuntime;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.FileChannel.MapMode;
-import libcore.io.ErrnoException;
import libcore.io.Libcore;
import libcore.io.Memory;
-import static libcore.io.OsConstants.MAP_PRIVATE;
-import static libcore.io.OsConstants.MAP_SHARED;
-import static libcore.io.OsConstants.PROT_READ;
-import static libcore.io.OsConstants.PROT_WRITE;
+import static android.system.OsConstants.MAP_PRIVATE;
+import static android.system.OsConstants.MAP_SHARED;
+import static android.system.OsConstants.PROT_READ;
+import static android.system.OsConstants.PROT_WRITE;
class MemoryBlock {
/**
diff --git a/luni/src/main/java/java/nio/MulticastMembershipHandler.java b/luni/src/main/java/java/nio/MulticastMembershipHandler.java
new file mode 100644
index 0000000..2fe8130
--- /dev/null
+++ b/luni/src/main/java/java/nio/MulticastMembershipHandler.java
@@ -0,0 +1,495 @@
+package java.nio;
+
+import android.system.StructGroupReq;
+import android.system.StructGroupSourceReq;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.nio.channels.MembershipKey;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import libcore.io.IoBridge;
+
+/**
+ * A helper class for {@link DatagramChannelImpl} that keeps track of multicast group
+ * memberships. This class is not threadsafe, and relies on the DatagramChannelImpl to synchronize.
+ *
+ * <p>See <a href="http://tools.ietf.org/html/rfc3678">RFC 3678</a> for context and terminology.
+ */
+final class MulticastMembershipHandler {
+
+ private final DatagramChannelImpl channel;
+ private final Map<Id, Membership> memberships = new HashMap<Id, Membership>();
+
+ MulticastMembershipHandler(DatagramChannelImpl channel) {
+ this.channel = channel;
+ }
+
+ /**
+ * The implementation for
+ * {@link java.nio.channels.MulticastChannel#join(InetAddress, NetworkInterface)}.
+ */
+ public MembershipKeyImpl addAnySourceMembership(
+ NetworkInterface networkInterface, InetAddress groupAddress) throws SocketException {
+
+ validateMulticastGroupArgs(groupAddress, networkInterface);
+ assertChannelOpen();
+
+ Id id = new Id(networkInterface, groupAddress);
+ Membership membership = memberships.get(id);
+ if (membership != null) {
+ return membership.getAnySourceMembershipKey();
+ }
+
+ // No existing membership found. Attempt to join.
+ StructGroupReq groupReq = makeGroupReq(groupAddress, networkInterface);
+ IoBridge.setSocketOption(channel.getFD(), IoBridge.JAVA_MCAST_JOIN_GROUP, groupReq);
+
+ // Record the membership and return the key.
+ membership = Membership.createAnySource(channel, networkInterface, groupAddress);
+ memberships.put(id, membership);
+ return membership.getAnySourceMembershipKey();
+ }
+
+ /**
+ * The implementation for
+ * {@link java.nio.channels.MulticastChannel#join(InetAddress, NetworkInterface, InetAddress)}.
+ */
+ public MembershipKeyImpl addSourceSpecificMembership(
+ NetworkInterface networkInterface, InetAddress groupAddress, InetAddress sourceAddress)
+ throws SocketException {
+
+ validateMulticastGroupArgs(groupAddress, networkInterface);
+ validateSourceAddress(sourceAddress);
+ validateAddressProtocolTheSame(groupAddress, sourceAddress);
+ assertChannelOpen();
+
+ Id id = new Id(networkInterface, groupAddress);
+ Membership membership = memberships.get(id);
+ if (membership != null) {
+ MembershipKeyImpl existingMembershipKey =
+ membership.getSourceSpecificMembershipKey(sourceAddress);
+ if (existingMembershipKey != null) {
+ return existingMembershipKey;
+ }
+ }
+
+ // No existing membership found. Attempt to join.
+ IoBridge.setSocketOption(channel.getFD(), IoBridge.JAVA_MCAST_JOIN_SOURCE_GROUP,
+ makeGroupSourceReq(groupAddress, networkInterface, sourceAddress));
+
+ if (membership == null) {
+ // Record the membership and return the key.
+ membership = Membership.createSourceSpecific(
+ channel, networkInterface, groupAddress, sourceAddress);
+ memberships.put(id, membership);
+ return membership.getSourceSpecificMembershipKey(sourceAddress);
+ } else {
+ // Add a new source to the existing membership.
+ return membership.addSource(sourceAddress);
+ }
+ }
+
+ /**
+ * The implementation for {@link MembershipKey#drop()}.
+ */
+ public void dropMembership(MembershipKeyImpl membershipKey) {
+ // For compatibility with the RI, this is one case where the membershipKey can no longer be
+ // valid.
+ if (!membershipKey.isValid()) {
+ return;
+ }
+ if (membershipKey.channel() != this.channel) {
+ throw new AssertionError("Bad membership key");
+ }
+ assertChannelOpen();
+
+ Id id = createId(membershipKey);
+ Membership membership = memberships.get(id);
+ if (membership == null) {
+ throw new AssertionError("Bad membership key" + membershipKey);
+ }
+
+ if (!membership.isSourceSpecific()) {
+ try {
+ StructGroupReq groupReq =
+ makeGroupReq(membershipKey.group(), membershipKey.networkInterface());
+ IoBridge.setSocketOption(channel.getFD(), IoBridge.JAVA_MCAST_LEAVE_GROUP, groupReq);
+ } catch (SocketException e) {
+ // TODO: Obtain opinion on how to report this, throw this or if it is safe to ignore.
+ throw new IllegalStateException(e);
+ }
+ memberships.remove(id);
+ } else {
+ StructGroupSourceReq groupSourceReq = makeGroupSourceReq(membershipKey.group(),
+ membershipKey.networkInterface(), membershipKey.sourceAddress());
+
+ try {
+ IoBridge.setSocketOption(
+ channel.getFD(), IoBridge.JAVA_MCAST_LEAVE_SOURCE_GROUP,
+ groupSourceReq);
+ } catch (SocketException e) {
+ // TODO: Obtain opinion on how to report this, throw this or if it is safe to ignore.
+ throw new IllegalStateException(e);
+ }
+
+ boolean isLast = membership.removeSource(membershipKey.sourceAddress());
+ if (isLast) {
+ memberships.remove(id);
+ }
+ }
+ membershipKey.invalidate();
+ }
+
+ /**
+ * The implementation for {@link MembershipKey#block(java.net.InetAddress)}.
+ */
+ public void block(MembershipKeyImpl membershipKey, InetAddress sourceAddress)
+ throws SocketException {
+ validateMembershipKey(membershipKey);
+ validateSourceAddress(sourceAddress);
+ validateAddressProtocolTheSame(membershipKey.group(), sourceAddress);
+ assertChannelOpen();
+
+ Membership membership = getMembershipForKey(membershipKey);
+ if (membership == null) {
+ throw new AssertionError("Bad membership key" + membershipKey);
+ }
+
+ if (membership.isBlocked(sourceAddress)) {
+ return;
+ }
+
+ IoBridge.setSocketOption(channel.getFD(), IoBridge.JAVA_MCAST_BLOCK_SOURCE,
+ makeGroupSourceReq(
+ membershipKey.group(), membershipKey.networkInterface(), sourceAddress));
+
+ membership.block(sourceAddress);
+ }
+
+ /**
+ * The implementation for {@link MembershipKey#unblock(java.net.InetAddress)}.
+ */
+ public void unblock(MembershipKeyImpl membershipKey, InetAddress sourceAddress) {
+ validateMembershipKey(membershipKey);
+ validateSourceAddress(sourceAddress);
+ validateAddressProtocolTheSame(membershipKey.group(), sourceAddress);
+ assertChannelOpen();
+
+ Membership membership = getMembershipForKey(membershipKey);
+ if (membership == null) {
+ throw new AssertionError("Bad membership key" + membershipKey);
+ }
+
+ if (!membership.isBlocked(sourceAddress)) {
+ throw new IllegalStateException(
+ "sourceAddress " + sourceAddress + " is not blocked for " + membership.debugId());
+ }
+
+ try {
+ IoBridge.setSocketOption(channel.getFD(), IoBridge.JAVA_MCAST_UNBLOCK_SOURCE,
+ makeGroupSourceReq(membershipKey.group(), membershipKey.networkInterface(),
+ sourceAddress));
+ } catch (SocketException e) {
+ throw new IllegalStateException(e);
+ }
+
+ membership.unblock(sourceAddress);
+ }
+
+ private Membership getMembershipForKey(MembershipKey membershipKey) {
+ Id id = createId(membershipKey);
+ Membership membership = memberships.get(id);
+ if (membership == null) {
+ throw new AssertionError("No membership found for id " + id);
+ }
+ return membership;
+ }
+
+ private void assertChannelOpen() {
+ if (!channel.isOpen()) {
+ throw new AssertionError("Channel is closed");
+ }
+ }
+
+ private void validateMembershipKey(MembershipKeyImpl membershipKey) {
+ if (membershipKey.channel() != this.channel) {
+ throw new AssertionError("Invalid or bad membership key");
+ }
+ if (!membershipKey.isValid()) {
+ throw new IllegalStateException("Membership key is no longer valid: " + membershipKey);
+ }
+ }
+
+ private static Id createId(MembershipKey membershipKey) {
+ return new Id(membershipKey.networkInterface(), membershipKey.group());
+ }
+
+ private static void validateSourceAddress(InetAddress sourceAddress) {
+ if (sourceAddress.isAnyLocalAddress()) {
+ throw new IllegalArgumentException(
+ "sourceAddress must not be a wildcard address, is " + sourceAddress);
+ }
+ if (sourceAddress.isMulticastAddress()) {
+ throw new IllegalArgumentException(
+ "sourceAddress must be a unicast address, is " + sourceAddress);
+ }
+ }
+
+ private static void validateMulticastGroupArgs(
+ InetAddress groupAddress, NetworkInterface networkInterface) throws SocketException {
+
+ if (groupAddress == null) {
+ // RI throws NullPointerException.
+ throw new NullPointerException("groupAddress == null");
+ }
+ if (networkInterface == null) {
+ // RI throws NullPointerException.
+ throw new NullPointerException("networkInterface == null");
+ }
+ if (!networkInterface.isLoopback() && !networkInterface.supportsMulticast()) {
+ throw new IllegalArgumentException(
+ "networkInterface " + networkInterface + " does not support multicast");
+ }
+ if (!groupAddress.isMulticastAddress()) {
+ throw new IllegalArgumentException("Not a multicast group: " + groupAddress);
+ }
+ }
+
+ private static void validateAddressProtocolTheSame(
+ InetAddress groupAddress, InetAddress sourceAddress) {
+
+ if (groupAddress.getClass() != sourceAddress.getClass()) {
+ throw new IllegalArgumentException("Mixed address types not permitted: groupAddress: " +
+ groupAddress + ", sourceAddress: " + sourceAddress);
+ }
+ }
+
+ private static StructGroupSourceReq makeGroupSourceReq(
+ InetAddress gsr_group, NetworkInterface networkInterface, InetAddress gsr_source) {
+ int gsr_interface = (networkInterface != null) ? networkInterface.getIndex() : 0;
+ return new StructGroupSourceReq(gsr_interface, gsr_group, gsr_source);
+ }
+
+ private static StructGroupReq makeGroupReq(InetAddress gr_group,
+ NetworkInterface networkInterface) {
+ int gr_interface = (networkInterface != null) ? networkInterface.getIndex() : 0;
+ return new StructGroupReq(gr_interface, gr_group);
+ }
+
+ /**
+ * Membership information associated with an {@link Id}. A membership can be one of two types:
+ * "source-specific" and "any-source". The two types a mutually exclusive for a given Id.
+ */
+ static final class Membership {
+
+ private final DatagramChannelImpl channel;
+ private final InetAddress groupAddress;
+ private final NetworkInterface networkInterface;
+
+ // Any-source membership key. Mutually exclusive with sourceSpecificMembershipKeys.
+ private final MembershipKeyImpl anySourceMembershipKey;
+ // Blocked source addresses for any-source memberships. Assigned when required.
+ private Set<InetAddress> blockedSourceAddresses;
+
+ // Source-specific membership keys. Mutually exclusive with anySourceMembershipKey.
+ private final Map<InetAddress, MembershipKeyImpl> sourceSpecificMembershipKeys;
+
+ /** Use {@link #createSourceSpecific} or {@link #createAnySource} to construct. */
+ private Membership(
+ DatagramChannelImpl channel,
+ InetAddress groupAddress,
+ NetworkInterface networkInterface,
+ MembershipKeyImpl anySourceMembershipKey,
+ Map<InetAddress, MembershipKeyImpl> sourceSpecificMembershipKeys) {
+
+ this.channel = channel;
+ this.groupAddress = groupAddress;
+ this.networkInterface = networkInterface;
+ this.anySourceMembershipKey = anySourceMembershipKey;
+ this.sourceSpecificMembershipKeys = sourceSpecificMembershipKeys;
+ }
+
+ /** Creates an any-source membership. */
+ public static Membership createAnySource(DatagramChannelImpl channel,
+ NetworkInterface networkInterface, InetAddress groupAddress) {
+
+ MembershipKeyImpl withoutSourceAddressKey =
+ new MembershipKeyImpl(channel, networkInterface, groupAddress, null /* sourceAddress */);
+ return new Membership(
+ channel, groupAddress, networkInterface, withoutSourceAddressKey,
+ null /* sourceSpecificMembershipKeys */);
+ }
+
+ /**
+ * Creates a source-specific membership. See {@link #addSource} to add additional source
+ * addresses.
+ */
+ public static Membership createSourceSpecific(DatagramChannelImpl channel,
+ NetworkInterface networkInterface, InetAddress groupAddress, InetAddress sourceAddress) {
+
+ Map<InetAddress, MembershipKeyImpl> withSourceKeys =
+ new HashMap<InetAddress, MembershipKeyImpl>();
+ Membership membership = new Membership(
+ channel, groupAddress, networkInterface, null /* anySourceMembershipKey */,
+ withSourceKeys);
+ membership.addSource(sourceAddress);
+ return membership;
+ }
+
+ /**
+ * Adds a new source address filter to an existing membership, returning the associated
+ * {@link MembershipKeyImpl}. Throws an {@code IllegalStateException} if this is an
+ * any-source membership.
+ */
+ public MembershipKeyImpl addSource(InetAddress sourceAddress) {
+ if (sourceSpecificMembershipKeys == null) {
+ throw new IllegalStateException(
+ "Can only add sources to source-specific memberships: " + debugId());
+ }
+
+ MembershipKeyImpl membershipKey =
+ new MembershipKeyImpl(channel, networkInterface, groupAddress, sourceAddress);
+ sourceSpecificMembershipKeys.put(sourceAddress, membershipKey);
+ return membershipKey;
+ }
+
+ /**
+ * Removes the specified {@code sourceAddress} from the set of filters. Returns {@code true} if
+ * the set of filters is now empty. Throws an {@code IllegalStateException} if this is an
+ * any-source membership.
+ */
+ public boolean removeSource(InetAddress sourceAddress) {
+ if (sourceSpecificMembershipKeys == null) {
+ throw new IllegalStateException(
+ "Can only remove sources from source-specific memberships: " + debugId());
+ }
+ sourceSpecificMembershipKeys.remove(sourceAddress);
+ return sourceSpecificMembershipKeys.isEmpty();
+ }
+
+ /**
+ * Returns {@code true} if the membership source-specific, false if it is any-source.
+ */
+ public boolean isSourceSpecific() {
+ return sourceSpecificMembershipKeys != null;
+ }
+
+ /**
+ * Returns the {@link MembershipKeyImpl} for this membership. Throws an
+ * {@code IllegalStateException} if this is not an any-source membership.
+ */
+ public MembershipKeyImpl getAnySourceMembershipKey() {
+ if (sourceSpecificMembershipKeys != null) {
+ throw new IllegalStateException(
+ "There an existing source-specific membership for " + debugId());
+ }
+ return anySourceMembershipKey;
+ }
+
+ /**
+ * Returns the {@link MembershipKeyImpl} for the specified {@code sourceAddress}. Throws an
+ * {@code IllegalStateException} if this is not a source-specific membership.
+ */
+ public MembershipKeyImpl getSourceSpecificMembershipKey(InetAddress sourceAddress) {
+ if (anySourceMembershipKey != null) {
+ throw new IllegalStateException("There an existing any-source membership for " + debugId());
+ }
+ return sourceSpecificMembershipKeys.get(sourceAddress);
+ }
+
+ /**
+ * Returns {@code true} if there is an existing block for the specified address. Throws an
+ * {@code IllegalStateException} if this is not an any-source membership.
+ */
+ public boolean isBlocked(InetAddress sourceAddress) {
+ if (anySourceMembershipKey == null) {
+ throw new IllegalStateException(
+ "block()/unblock() are only applicable for any-source memberships: " + debugId());
+ }
+ return blockedSourceAddresses != null && blockedSourceAddresses.contains(sourceAddress);
+ }
+
+ /**
+ * Adds a blocked address to this membership. Throws an {@code IllegalStateException} if
+ * the address is already blocked. Throws an {@code IllegalStateException} if this is not an
+ * any-source membership.
+ */
+ public void block(InetAddress sourceAddress) {
+ if (anySourceMembershipKey == null) {
+ throw new IllegalStateException(
+ "block() is not supported for source-specific group memberships: " + debugId());
+ }
+ if (blockedSourceAddresses == null) {
+ blockedSourceAddresses = new HashSet<InetAddress>();
+ }
+ if (!blockedSourceAddresses.add(sourceAddress)) {
+ throw new IllegalStateException(
+ "Could not block " + sourceAddress + ": it was already blocked for " + debugId());
+ }
+ }
+
+ /**
+ * Removes a blocked address from this membership. Throws an {@code IllegalStateException} if
+ * the address is not blocked. Throws an {@code IllegalStateException} if this is not an
+ * any-source membership.
+ */
+ public void unblock(InetAddress sourceAddress) {
+ if (anySourceMembershipKey == null) {
+ throw new IllegalStateException(
+ "unblock() is not supported for source-specific group memberships: " + debugId());
+ }
+ if (blockedSourceAddresses == null || !blockedSourceAddresses.remove(sourceAddress)) {
+ throw new IllegalStateException(
+ "Could not unblock " + sourceAddress + ": it was not blocked for " + debugId());
+ }
+ }
+
+ public String debugId() {
+ return "<" + networkInterface + ":" + groupAddress + ">";
+ }
+
+ }
+
+ /** An identifier for a multicast group membership, independent of membership type. */
+ private static final class Id {
+
+ private final InetAddress groupAddress;
+ private final NetworkInterface networkInterface;
+
+ public Id(NetworkInterface networkInterface, InetAddress groupAddress) {
+ this.groupAddress = groupAddress;
+ this.networkInterface = networkInterface;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Id)) {
+ return false;
+ }
+
+ Id id = (Id) o;
+
+ if (!groupAddress.equals(id.groupAddress)) {
+ return false;
+ }
+ if (!networkInterface.equals(id.networkInterface)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = groupAddress.hashCode();
+ result = 31 * result + networkInterface.hashCode();
+ return result;
+ }
+ }
+}
diff --git a/luni/src/main/java/java/nio/NIOAccess.java b/luni/src/main/java/java/nio/NIOAccess.java
index 12af44d..ddb102e 100644
--- a/luni/src/main/java/java/nio/NIOAccess.java
+++ b/luni/src/main/java/java/nio/NIOAccess.java
@@ -24,17 +24,11 @@
/**
* Returns the underlying native pointer to the data of the given
* Buffer starting at the Buffer's current position, or 0 if the
- * Buffer is not backed by native heap storage. Note that this is
- * different than what the Harmony implementation calls a "base
- * address."
- *
- * @param b the Buffer to be queried
- * @return the native pointer to the Buffer's data at its current
- * position, or 0 if there is none
+ * Buffer is not backed by native heap storage.
*/
static long getBasePointer(Buffer b) {
long address = b.effectiveDirectAddress;
- if (address == 0) {
+ if (address == 0L) {
return 0L;
}
return address + (b.position << b._elementSizeShift);
@@ -43,10 +37,6 @@
/**
* Returns the underlying Java array containing the data of the
* given Buffer, or null if the Buffer is not backed by a Java array.
- *
- * @param b the Buffer to be queried
- * @return the Java array containing the Buffer's data, or null if
- * there is none
*/
static Object getBaseArray(Buffer b) {
return b.hasArray() ? b.array() : null;
@@ -58,9 +48,6 @@
* the actual start of the data. The start of the data takes into
* account the Buffer's current position. This method is only
* meaningful if getBaseArray() returns non-null.
- *
- * @param b the Buffer to be queried
- * @return the data offset in bytes to the start of this Buffer's data
*/
static int getBaseArrayOffset(Buffer b) {
return b.hasArray() ? ((b.arrayOffset() + b.position) << b._elementSizeShift) : 0;
diff --git a/luni/src/main/java/java/nio/NioUtils.java b/luni/src/main/java/java/nio/NioUtils.java
index a1a46b6..51adddb 100644
--- a/luni/src/main/java/java/nio/NioUtils.java
+++ b/luni/src/main/java/java/nio/NioUtils.java
@@ -16,8 +16,14 @@
package java.nio;
+import java.io.Closeable;
import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
+import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
+import java.util.Set;
/**
* @hide internal use only
@@ -43,8 +49,8 @@
/**
* Helps bridge between io and nio.
*/
- public static FileChannel newFileChannel(Object stream, FileDescriptor fd, int mode) {
- return new FileChannelImpl(stream, fd, mode);
+ public static FileChannel newFileChannel(Closeable ioObject, FileDescriptor fd, int mode) {
+ return new FileChannelImpl(ioObject, fd, mode);
}
/**
@@ -62,4 +68,66 @@
public static int unsafeArrayOffset(ByteBuffer b) {
return ((ByteArrayBuffer) b).arrayOffset;
}
+
+ /**
+ * Sets the supplied option on the channel to have the value if option is a member of
+ * allowedOptions.
+ *
+ * @throws IOException
+ * if the value could not be set due to IO errors.
+ * @throws IllegalArgumentException
+ * if the socket option or the value is invalid.
+ * @throws UnsupportedOperationException
+ * if the option is not a member of allowedOptions.
+ * @throws ClosedChannelException
+ * if the channel is closed
+ */
+ public static <T> void setSocketOption(
+ FileDescriptorChannel channel, Set<SocketOption<?>> allowedOptions,
+ SocketOption<T> option, T value)
+ throws IOException {
+
+ if (!(option instanceof StandardSocketOptions.SocketOptionImpl)) {
+ throw new IllegalArgumentException("SocketOption must come from StandardSocketOptions");
+ }
+ if (!allowedOptions.contains(option)) {
+ throw new UnsupportedOperationException(
+ option + " is not supported for this type of socket");
+ }
+ if (!channel.getFD().valid()) {
+ throw new ClosedChannelException();
+ }
+ ((StandardSocketOptions.SocketOptionImpl<T>) option).setValue(channel.getFD(), value);
+ }
+
+ /**
+ * Gets the supplied option from the channel if option is a member of allowedOptions.
+ *
+ * @throws IOException
+ * if the value could not be read due to IO errors.
+ * @throws IllegalArgumentException
+ * if the socket option is invalid.
+ * @throws UnsupportedOperationException
+ * if the option is not a member of allowedOptions.
+ * @throws ClosedChannelException
+ * if the channel is closed
+ */
+ public static <T> T getSocketOption(
+ FileDescriptorChannel channel, Set<SocketOption<?>> allowedOptions,
+ SocketOption<T> option)
+ throws IOException {
+
+ if (!(option instanceof StandardSocketOptions.SocketOptionImpl)) {
+ throw new IllegalArgumentException("SocketOption must come from StandardSocketOptions");
+ }
+ if (!allowedOptions.contains(option)) {
+ throw new UnsupportedOperationException(
+ option + " is not supported for this type of socket");
+ }
+ if (!channel.getFD().valid()) {
+ throw new ClosedChannelException();
+ }
+ return ((StandardSocketOptions.SocketOptionImpl<T>) option).getValue(channel.getFD());
+ }
+
}
diff --git a/luni/src/main/java/java/nio/PipeImpl.java b/luni/src/main/java/java/nio/PipeImpl.java
index d4dde3b..ebc8a91 100644
--- a/luni/src/main/java/java/nio/PipeImpl.java
+++ b/luni/src/main/java/java/nio/PipeImpl.java
@@ -16,16 +16,16 @@
package java.nio;
+import android.system.ErrnoException;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.Pipe;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/*
* Implements {@link java.nio.channels.Pipe}.
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index d63fa63..efa8712 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -15,33 +15,37 @@
*/
package java.nio;
+import android.system.ErrnoException;
+import android.system.StructPollfd;
import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
import java.io.IOException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.IllegalSelectorException;
-import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
-import static java.nio.channels.SelectionKey.*;
import java.nio.channels.Selector;
-import java.nio.channels.SocketChannel;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.AbstractSelectionKey;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.UnsafeArrayList;
-import libcore.io.ErrnoException;
import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import libcore.io.StructPollfd;
-import libcore.util.EmptyArray;
-import static libcore.io.OsConstants.*;
+
+import static android.system.OsConstants.EINTR;
+import static android.system.OsConstants.POLLHUP;
+import static android.system.OsConstants.POLLIN;
+import static android.system.OsConstants.POLLOUT;
+import static java.nio.channels.SelectionKey.OP_ACCEPT;
+import static java.nio.channels.SelectionKey.OP_CONNECT;
+import static java.nio.channels.SelectionKey.OP_READ;
+import static java.nio.channels.SelectionKey.OP_WRITE;
/*
* Default implementation of java.nio.channels.Selector
@@ -321,6 +325,7 @@
try {
Libcore.os.write(wakeupOut, new byte[] { 1 }, 0, 1);
} catch (ErrnoException ignored) {
+ } catch (InterruptedIOException ignored) {
}
return this;
}
diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
index 3fb61a3..d2cbf36 100644
--- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
@@ -17,23 +17,28 @@
package java.nio;
+import android.system.ErrnoException;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.net.PlainServerSocketImpl;
+import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
-import java.net.SocketImpl;
+import java.net.SocketOption;
import java.net.SocketTimeoutException;
+import java.net.StandardSocketOptions;
+import java.nio.channels.AlreadyBoundException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
-import libcore.io.ErrnoException;
+import java.nio.channels.UnresolvedAddressException;
+import java.nio.channels.UnsupportedAddressTypeException;
+import java.util.Set;
import libcore.io.IoUtils;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* The default ServerSocketChannel.
@@ -41,31 +46,79 @@
final class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorChannel {
private final ServerSocketAdapter socket;
- private final SocketImpl impl;
-
- private boolean isBound = false;
private final Object acceptLock = new Object();
public ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
super(sp);
this.socket = new ServerSocketAdapter(this);
- this.impl = socket.getImpl$();
}
@Override public ServerSocket socket() {
return socket;
}
- @Override public SocketChannel accept() throws IOException {
+ /** @hide Until ready for a public API change */
+ @Override
+ public final ServerSocketChannel bind(SocketAddress localAddr, int backlog) throws IOException {
if (!isOpen()) {
throw new ClosedChannelException();
}
- if (!isBound) {
+ if (socket.isBound()) {
+ throw new AlreadyBoundException();
+ }
+ if (localAddr != null) {
+ if (!(localAddr instanceof InetSocketAddress)) {
+ throw new UnsupportedAddressTypeException();
+ }
+ InetSocketAddress localInetAddress = (InetSocketAddress) localAddr;
+ if (localInetAddress.isUnresolved()) {
+ throw new UnresolvedAddressException();
+ }
+ }
+
+ socket.bind(localAddr, backlog);
+ return this;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public SocketAddress getLocalAddress() throws IOException {
+ if (!isOpen()) {
+ throw new ClosedChannelException();
+ }
+ return socket.getLocalSocketAddress();
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ return NioUtils.getSocketOption(this, StandardSocketOptions.SERVER_SOCKET_OPTIONS, option);
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> ServerSocketChannel setOption(SocketOption<T> option, T value) throws IOException {
+ NioUtils.setSocketOption(this, StandardSocketOptions.SERVER_SOCKET_OPTIONS, option, value);
+ return this;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return StandardSocketOptions.SERVER_SOCKET_OPTIONS;
+ }
+
+ @Override
+ public SocketChannel accept() throws IOException {
+ if (!isOpen()) {
+ throw new ClosedChannelException();
+ }
+ if (!socket.isBound()) {
throw new NotYetBoundException();
}
- // Create an empty socket channel. This will be populated by ServerSocketAdapter.accept.
+ // Create an empty socket channel. This will be populated by ServerSocketAdapter.implAccept.
SocketChannelImpl result = new SocketChannelImpl(provider(), false);
try {
begin();
@@ -81,9 +134,9 @@
}
}
} finally {
- end(result.socket().isConnected());
+ end(result.isConnected());
}
- return result.socket().isConnected() ? result : null;
+ return result.isConnected() ? result : null;
}
private boolean shouldThrowSocketTimeoutExceptionFromAccept(SocketTimeoutException e) {
@@ -100,17 +153,19 @@
}
@Override protected void implConfigureBlocking(boolean blocking) throws IOException {
- IoUtils.setBlocking(impl.getFD$(), blocking);
+ IoUtils.setBlocking(socket.getFD$(), blocking);
}
+ @Override
synchronized protected void implCloseSelectableChannel() throws IOException {
if (!socket.isClosed()) {
socket.close();
}
}
+ @Override
public FileDescriptor getFD() {
- return impl.getFD$();
+ return socket.getFD$();
}
private static class ServerSocketAdapter extends ServerSocket {
@@ -120,13 +175,8 @@
this.channelImpl = aChannelImpl;
}
- @Override public void bind(SocketAddress localAddress, int backlog) throws IOException {
- super.bind(localAddress, backlog);
- channelImpl.isBound = true;
- }
-
@Override public Socket accept() throws IOException {
- if (!channelImpl.isBound) {
+ if (!isBound()) {
throw new IllegalBlockingModeException();
}
SocketChannel sc = channelImpl.accept();
@@ -142,9 +192,12 @@
try {
synchronized (this) {
super.implAccept(clientSocket);
- clientSocketChannel.setConnected();
- clientSocketChannel.setBound(true);
- clientSocketChannel.finishAccept();
+
+ // Sync the client socket's associated channel state with the Socket and OS.
+ InetSocketAddress remoteAddress =
+ new InetSocketAddress(
+ clientSocket.getInetAddress(), clientSocket.getPort());
+ clientSocketChannel.onAccept(remoteAddress, false /* updateSocketState */);
}
connectOK = true;
} finally {
@@ -159,23 +212,17 @@
return channelImpl;
}
- @Override public boolean isBound() {
- return channelImpl.isBound;
- }
-
- @Override public void bind(SocketAddress localAddress) throws IOException {
- super.bind(localAddress);
- channelImpl.isBound = true;
- }
-
@Override public void close() throws IOException {
synchronized (channelImpl) {
+ super.close();
if (channelImpl.isOpen()) {
channelImpl.close();
- } else {
- super.close();
}
}
}
+
+ private FileDescriptor getFD$() {
+ return super.getImpl$().getFD$();
+ }
}
}
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index b05e36c..7c3cd78 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -17,9 +17,12 @@
package java.nio;
+import android.system.ErrnoException;
import java.io.FileDescriptor;
-import java.io.IOException;
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
import java.io.InputStream;
+import java.io.IOException;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Inet4Address;
@@ -29,8 +32,10 @@
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.net.SocketOption;
import java.net.SocketUtils;
-import java.net.UnknownHostException;
+import java.net.StandardSocketOptions;
+import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ConnectionPendingException;
@@ -38,15 +43,15 @@
import java.nio.channels.NoConnectionPendingException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SocketChannel;
+import java.nio.channels.spi.SelectorProvider;
import java.nio.channels.UnresolvedAddressException;
import java.nio.channels.UnsupportedAddressTypeException;
-import java.nio.channels.spi.SelectorProvider;
import java.util.Arrays;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
+import java.util.Set;
import libcore.io.IoBridge;
import libcore.io.IoUtils;
-import static libcore.io.OsConstants.*;
+import libcore.io.Libcore;
+import static android.system.OsConstants.*;
/*
* The default implementation class of java.nio.channels.SocketChannel.
@@ -74,6 +79,7 @@
// The address to be connected.
private InetSocketAddress connectAddress = null;
+ // The local address the socket is bound to.
private InetAddress localAddress = null;
private int localPort;
@@ -133,22 +139,89 @@
return socket;
}
+ /** @hide Until ready for a public API change */
+ @Override
+ synchronized public final SocketChannel bind(SocketAddress local) throws IOException {
+ if (!isOpen()) {
+ throw new ClosedChannelException();
+ }
+ if (isBound) {
+ throw new AlreadyBoundException();
+ }
+
+ if (local == null) {
+ local = new InetSocketAddress(Inet4Address.ANY, 0);
+ } else if (!(local instanceof InetSocketAddress)) {
+ throw new UnsupportedAddressTypeException();
+ }
+
+ InetSocketAddress localAddress = (InetSocketAddress) local;
+ if (localAddress.isUnresolved()) {
+ throw new UnresolvedAddressException();
+ }
+ IoBridge.bind(fd, localAddress.getAddress(), localAddress.getPort());
+ onBind(true /* updateSocketState */);
+ return this;
+ }
+
+ /**
+ * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when
+ * some or all of the bound state has been left to the OS to decide, or when the Socket handled
+ * bind() or connect().
+ *
+ * @param updateSocketState
+ * if the associated socket (if present) needs to be updated
+ * @hide package visible for other nio classes
+ */
+ void onBind(boolean updateSocketState) {
+ SocketAddress sa;
+ try {
+ sa = Libcore.os.getsockname(fd);
+ } catch (ErrnoException errnoException) {
+ throw new AssertionError(errnoException);
+ }
+ isBound = true;
+ InetSocketAddress localSocketAddress = (InetSocketAddress) sa;
+ localAddress = localSocketAddress.getAddress();
+ localPort = localSocketAddress.getPort();
+ if (updateSocketState && socket != null) {
+ socket.onBind(localAddress, localPort);
+ }
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ synchronized public SocketAddress getLocalAddress() throws IOException {
+ if (!isOpen()) {
+ throw new ClosedChannelException();
+ }
+ return isBound ? new InetSocketAddress(localAddress, localPort) : null;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ return NioUtils.getSocketOption(this, StandardSocketOptions.SOCKET_OPTIONS, option);
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> SocketChannel setOption(SocketOption<T> option, T value) throws IOException {
+ NioUtils.setSocketOption(this, StandardSocketOptions.SOCKET_OPTIONS, option, value);
+ return this;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return StandardSocketOptions.SOCKET_OPTIONS;
+ }
+
@Override
synchronized public boolean isConnected() {
return status == SOCKET_STATUS_CONNECTED;
}
- /*
- * Status setting used by other class.
- */
- synchronized void setConnected() {
- status = SOCKET_STATUS_CONNECTED;
- }
-
- void setBound(boolean flag) {
- isBound = flag;
- }
-
@Override
synchronized public boolean isConnectionPending() {
return status == SOCKET_STATUS_PENDING;
@@ -169,16 +242,22 @@
normalAddr = InetAddress.getLocalHost();
}
+ boolean isBlocking = isBlocking();
boolean finished = false;
+ int newStatus;
try {
- if (isBlocking()) {
+ if (isBlocking) {
begin();
}
- finished = IoBridge.connect(fd, normalAddr, port);
- isBound = finished;
+ // When in blocking mode, IoBridge.connect() will return without an exception when the
+ // socket is connected. When in non-blocking mode it will return without an exception
+ // without knowing the result of the connection attempt, which could still be going on.
+ IoBridge.connect(fd, normalAddr, port);
+ newStatus = isBlocking ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_PENDING;
+ finished = true;
} catch (IOException e) {
if (isEINPROGRESS(e)) {
- status = SOCKET_STATUS_PENDING;
+ newStatus = SOCKET_STATUS_PENDING;
} else {
if (isOpen()) {
close();
@@ -187,26 +266,36 @@
throw e;
}
} finally {
- if (isBlocking()) {
+ if (isBlocking) {
end(finished);
}
}
- initLocalAddressAndPort();
- connectAddress = inetSocketAddress;
- if (socket != null) {
- socket.socketImpl().initRemoteAddressAndPort(connectAddress.getAddress(),
- connectAddress.getPort());
+ // If the channel was not bound, a connection attempt will have caused an implicit bind() to
+ // take place. Keep the local address state held by the channel and the socket up to date.
+ if (!isBound) {
+ onBind(true /* updateSocketState */);
}
- synchronized (this) {
- if (isBlocking()) {
- status = (finished ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_UNCONNECTED);
- } else {
- status = SOCKET_STATUS_PENDING;
- }
+ // Keep the connected state held by the channel and the socket up to date.
+ onConnectStatusChanged(inetSocketAddress, newStatus, true /* updateSocketState */);
+
+ return status == SOCKET_STATUS_CONNECTED;
+ }
+
+ /**
+ * Initialise the connect() state with the supplied information.
+ *
+ * @param updateSocketState
+ * if the associated socket (if present) needs to be updated
+ * @hide package visible for other nio classes
+ */
+ void onConnectStatusChanged(InetSocketAddress address, int status, boolean updateSocketState) {
+ this.status = status;
+ connectAddress = address;
+ if (status == SOCKET_STATUS_CONNECTED && updateSocketState && socket != null) {
+ socket.onConnect(connectAddress.getAddress(), connectAddress.getPort());
}
- return finished;
}
private boolean isEINPROGRESS(IOException e) {
@@ -222,21 +311,6 @@
return false;
}
- private void initLocalAddressAndPort() {
- SocketAddress sa;
- try {
- sa = Libcore.os.getsockname(fd);
- } catch (ErrnoException errnoException) {
- throw new AssertionError(errnoException);
- }
- InetSocketAddress isa = (InetSocketAddress) sa;
- localAddress = isa.getAddress();
- localPort = isa.getPort();
- if (socket != null) {
- socket.socketImpl().initLocalPort(localPort);
- }
- }
-
@Override
public boolean finishConnect() throws IOException {
synchronized (this) {
@@ -257,7 +331,6 @@
InetAddress inetAddress = connectAddress.getAddress();
int port = connectAddress.getPort();
finished = IoBridge.isConnected(fd, inetAddress, port, 0, 0); // Return immediately.
- isBound = finished;
} catch (ConnectException e) {
if (isOpen()) {
close();
@@ -270,15 +343,13 @@
synchronized (this) {
status = (finished ? SOCKET_STATUS_CONNECTED : status);
- isBound = finished;
+ if (finished && socket != null) {
+ socket.onConnect(connectAddress.getAddress(), connectAddress.getPort());
+ }
}
return finished;
}
- void finishAccept() {
- initLocalAddressAndPort();
- }
-
@Override
public int read(ByteBuffer dst) throws IOException {
dst.checkWritable();
@@ -447,23 +518,17 @@
}
/*
- * Get local address.
- */
- public InetAddress getLocalAddress() throws UnknownHostException {
- return isBound ? localAddress : Inet4Address.ANY;
- }
-
- /*
* Do really closing action here.
*/
@Override
protected synchronized void implCloseSelectableChannel() throws IOException {
if (status != SOCKET_STATUS_CLOSED) {
status = SOCKET_STATUS_CLOSED;
+ // IoBridge.closeAndSignalBlockedThreads(fd) is idempotent: It is safe to call on an
+ // already-closed file descriptor.
+ IoBridge.closeAndSignalBlockedThreads(fd);
if (socket != null && !socket.isClosed()) {
- socket.close();
- } else {
- IoBridge.closeSocket(fd);
+ socket.onClose();
}
}
}
@@ -479,6 +544,12 @@
return fd;
}
+ /* @hide used by ServerSocketChannelImpl to sync channel state during accept() */
+ public void onAccept(InetSocketAddress remoteAddress, boolean updateSocketState) {
+ onBind(updateSocketState);
+ onConnectStatusChanged(remoteAddress, SOCKET_STATUS_CONNECTED, updateSocketState);
+ }
+
/*
* Adapter classes for internal socket.
*/
@@ -486,15 +557,24 @@
private final SocketChannelImpl channel;
private final PlainSocketImpl socketImpl;
- SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel) throws SocketException {
+ SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel)
+ throws SocketException {
super(socketImpl);
this.socketImpl = socketImpl;
this.channel = channel;
SocketUtils.setCreated(this);
- }
- PlainSocketImpl socketImpl() {
- return socketImpl;
+ // Sync state socket state with the channel it is being created from
+ if (channel.isBound) {
+ onBind(channel.localAddress, channel.localPort);
+ }
+ if (channel.isConnected()) {
+ onConnect(channel.connectAddress.getAddress(), channel.connectAddress.getPort());
+ }
+ if (!channel.isOpen()) {
+ onClose();
+ }
+
}
@Override
@@ -503,25 +583,6 @@
}
@Override
- public boolean isBound() {
- return channel.isBound;
- }
-
- @Override
- public boolean isConnected() {
- return channel.isConnected();
- }
-
- @Override
- public InetAddress getLocalAddress() {
- try {
- return channel.getLocalAddress();
- } catch (UnknownHostException e) {
- return null;
- }
- }
-
- @Override
public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
if (!channel.isBlocking()) {
throw new IllegalBlockingModeException();
@@ -530,10 +591,11 @@
throw new AlreadyConnectedException();
}
super.connect(remoteAddr, timeout);
- channel.initLocalAddressAndPort();
+ channel.onBind(false);
if (super.isConnected()) {
- channel.setConnected();
- channel.isBound = super.isBound();
+ InetSocketAddress remoteInetAddress = (InetSocketAddress) remoteAddr;
+ channel.onConnectStatusChanged(
+ remoteInetAddress, SOCKET_STATUS_CONNECTED, false /* updateSocketState */);
}
}
@@ -546,47 +608,29 @@
throw new ConnectionPendingException();
}
super.bind(localAddr);
- channel.initLocalAddressAndPort();
- channel.isBound = true;
+ channel.onBind(false);
}
@Override
public void close() throws IOException {
synchronized (channel) {
+ super.close();
if (channel.isOpen()) {
+ // channel.close() recognizes the socket is closed and avoids recursion. There
+ // is no channel.onClose() because the "closed" field is private.
channel.close();
- } else {
- super.close();
}
- channel.status = SocketChannelImpl.SOCKET_STATUS_CLOSED;
}
}
@Override
public OutputStream getOutputStream() throws IOException {
- checkOpenAndConnected();
- if (isOutputShutdown()) {
- throw new SocketException("Socket output is shutdown");
- }
- return new SocketChannelOutputStream(channel);
+ return new BlockingCheckOutputStream(super.getOutputStream(), channel);
}
@Override
public InputStream getInputStream() throws IOException {
- checkOpenAndConnected();
- if (isInputShutdown()) {
- throw new SocketException("Socket input is shutdown");
- }
- return new SocketChannelInputStream(channel);
- }
-
- private void checkOpenAndConnected() throws SocketException {
- if (!channel.isOpen()) {
- throw new SocketException("Socket is closed");
- }
- if (!channel.isConnected()) {
- throw new SocketException("Socket is not connected");
- }
+ return new BlockingCheckInputStream(super.getInputStream(), channel);
}
@Override
@@ -596,86 +640,92 @@
}
/*
- * This output stream delegates all operations to the associated channel.
* Throws an IllegalBlockingModeException if the channel is in non-blocking
* mode when performing write operations.
*/
- private static class SocketChannelOutputStream extends OutputStream {
+ private static class BlockingCheckOutputStream extends FilterOutputStream {
private final SocketChannel channel;
- public SocketChannelOutputStream(SocketChannel channel) {
+ public BlockingCheckOutputStream(OutputStream out, SocketChannel channel) {
+ super(out);
this.channel = channel;
}
- /*
- * Closes this stream and channel.
- *
- * @throws IOException thrown if an error occurs during the close
- */
- @Override
- public void close() throws IOException {
- channel.close();
- }
-
@Override
public void write(byte[] buffer, int offset, int byteCount) throws IOException {
- Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
- ByteBuffer buf = ByteBuffer.wrap(buffer, offset, byteCount);
- if (!channel.isBlocking()) {
- throw new IllegalBlockingModeException();
- }
- channel.write(buf);
+ checkBlocking();
+ out.write(buffer, offset, byteCount);
}
@Override
public void write(int oneByte) throws IOException {
+ checkBlocking();
+ out.write(oneByte);
+ }
+
+ @Override
+ public void write(byte[] buffer) throws IOException {
+ checkBlocking();
+ out.write(buffer);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ // channel.close() recognizes the socket is closed and avoids recursion. There is no
+ // channel.onClose() because the "closed" field is private.
+ channel.close();
+ }
+
+ private void checkBlocking() {
if (!channel.isBlocking()) {
throw new IllegalBlockingModeException();
}
- ByteBuffer buffer = ByteBuffer.allocate(1);
- buffer.put(0, (byte) (oneByte & 0xFF));
- channel.write(buffer);
}
}
/*
- * This input stream delegates all operations to the associated channel.
* Throws an IllegalBlockingModeException if the channel is in non-blocking
* mode when performing read operations.
*/
- private static class SocketChannelInputStream extends InputStream {
+ private static class BlockingCheckInputStream extends FilterInputStream {
private final SocketChannel channel;
- public SocketChannelInputStream(SocketChannel channel) {
+ public BlockingCheckInputStream(InputStream in, SocketChannel channel) {
+ super(in);
this.channel = channel;
}
- /*
- * Closes this stream and channel.
- */
- @Override
- public void close() throws IOException {
- channel.close();
- }
-
@Override
public int read() throws IOException {
- if (!channel.isBlocking()) {
- throw new IllegalBlockingModeException();
- }
- ByteBuffer buf = ByteBuffer.allocate(1);
- int result = channel.read(buf);
- return (result == -1) ? result : (buf.get(0) & 0xff);
+ checkBlocking();
+ return in.read();
}
@Override
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
- Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
+ checkBlocking();
+ return in.read(buffer, byteOffset, byteCount);
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ checkBlocking();
+ return in.read(buffer);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ // channel.close() recognizes the socket is closed and avoids recursion. There is no
+ // channel.onClose() because the "closed" field is private.
+ channel.close();
+ }
+
+ private void checkBlocking() {
if (!channel.isBlocking()) {
throw new IllegalBlockingModeException();
}
- ByteBuffer buf = ByteBuffer.wrap(buffer, byteOffset, byteCount);
- return channel.read(buf);
}
}
}
diff --git a/luni/src/main/java/java/nio/channels/AlreadyBoundException.java b/luni/src/main/java/java/nio/channels/AlreadyBoundException.java
new file mode 100644
index 0000000..0a35fc3
--- /dev/null
+++ b/luni/src/main/java/java/nio/channels/AlreadyBoundException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 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.nio.channels;
+
+/**
+ * An {@code AlreadyBoundException} is thrown when an attempt is made to bind a NetworkChannel that
+ * is already bound.
+ *
+ * @hide Until ready for a public API change
+ */
+public class AlreadyBoundException extends IllegalStateException {
+
+ private static final long serialVersionUID = 6796072983322737592L;
+
+ public AlreadyBoundException() {
+ }
+}
diff --git a/luni/src/main/java/java/nio/channels/DatagramChannel.java b/luni/src/main/java/java/nio/channels/DatagramChannel.java
index 486b168..3a5d1cc 100644
--- a/luni/src/main/java/java/nio/channels/DatagramChannel.java
+++ b/luni/src/main/java/java/nio/channels/DatagramChannel.java
@@ -19,10 +19,14 @@
import java.io.IOException;
import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
import java.net.SocketAddress;
+import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
+import java.util.Set;
/**
* A {@code DatagramChannel} is a selectable channel that represents a partial
@@ -40,7 +44,7 @@
* same time.
*/
public abstract class DatagramChannel extends AbstractSelectableChannel
- implements ByteChannel, ScatteringByteChannel, GatheringByteChannel {
+ implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, MulticastChannel {
/**
* Constructs a new {@code DatagramChannel}.
@@ -88,6 +92,64 @@
*/
public abstract DatagramSocket socket();
+ /** @hide Until ready for a public API change */
+ @Override
+ public DatagramChannel bind(SocketAddress local) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public SocketAddress getLocalAddress() throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> DatagramChannel setOption(SocketOption<T> option, T value) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public MembershipKey join(InetAddress groupAddress, NetworkInterface networkInterface)
+ throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public MembershipKey join(InetAddress groupAddress, NetworkInterface networkInterface,
+ InetAddress sourceAddress) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
/**
* Returns whether this channel's socket is connected or not.
*
diff --git a/luni/src/main/java/java/nio/channels/FileChannel.java b/luni/src/main/java/java/nio/channels/FileChannel.java
index 0719a99..e5f2862c 100644
--- a/luni/src/main/java/java/nio/channels/FileChannel.java
+++ b/luni/src/main/java/java/nio/channels/FileChannel.java
@@ -76,8 +76,9 @@
* stream and vice versa; this includes modifications to the file position,
* content, size, etc.
*/
+// TODO: Remove ByteChannel when SeekableByteChannel is unhidden.
public abstract class FileChannel extends AbstractInterruptibleChannel
- implements GatheringByteChannel, ScatteringByteChannel, ByteChannel {
+ implements GatheringByteChannel, ScatteringByteChannel, ByteChannel, SeekableByteChannel {
/**
* {@code MapMode} defines file mapping mode constants.
@@ -281,68 +282,21 @@
long position, long size) throws IOException;
/**
- * Returns the current value of the file position pointer.
- *
- * @return the current position as a positive integer number of bytes from
- * the start of the file.
- * @throws ClosedChannelException
- * if this channel is closed.
- * @throws IOException
- * if another I/O error occurs.
+ * {@inheritDoc}
*/
+ @Override
public abstract long position() throws IOException;
/**
- * Sets the file position pointer to a new value.
- * <p>
- * The argument is the number of bytes counted from the start of the file.
- * The position cannot be set to a value that is negative. The new position
- * can be set beyond the current file size. If set beyond the current file
- * size, attempts to read will return end of file. Write operations will
- * succeed but they will fill the bytes between the current end of file and
- * the new position with the required number of (unspecified) byte values.
- *
- * @param offset
- * the new file position, in bytes.
- * @return the receiver.
- * @throws IllegalArgumentException
- * if the new position is negative.
- * @throws ClosedChannelException
- * if this channel is closed.
- * @throws IOException
- * if another I/O error occurs.
+ * {@inheritDoc}
*/
- public abstract FileChannel position(long offset) throws IOException;
+ @Override
+ public abstract FileChannel position(long newPosition) throws IOException;
/**
- * Reads bytes from this file channel into the given buffer.
- * <p>
- * The maximum number of bytes that will be read is the remaining number of
- * bytes in the buffer when the method is invoked. The bytes will be copied
- * into the buffer starting at the buffer's current position.
- * <p>
- * The call may block if other threads are also attempting to read from this
- * channel.
- * <p>
- * Upon completion, the buffer's position is set to the end of the bytes
- * that have been read. The buffer's limit is not changed.
- *
- * @param buffer
- * the byte buffer to receive the bytes.
- * @return the number of bytes actually read.
- * @throws AsynchronousCloseException
- * if another thread closes the channel during the read.
- * @throws ClosedByInterruptException
- * if another thread interrupts the calling thread during the
- * read.
- * @throws ClosedChannelException
- * if this channel is closed.
- * @throws IOException
- * if another I/O error occurs, details are in the message.
- * @throws NonReadableChannelException
- * if the channel has not been opened in a mode that permits
- * reading.
+ * {@inheritDoc}
*/
+ @Override
public abstract int read(ByteBuffer buffer) throws IOException;
/**
@@ -362,7 +316,7 @@
* the buffer to receive the bytes.
* @param position
* the (non-negative) position at which to read the bytes.
- * @return the number of bytes actually read.
+ * @return the number of bytes actually read, or -1 if the end of the file has been reached.
* @throws AsynchronousCloseException
* if this channel is closed by another thread while this method
* is executing.
@@ -398,7 +352,7 @@
*
* @param buffers
* the array of byte buffers into which the bytes will be copied.
- * @return the number of bytes actually read.
+ * @return the number of bytes actually read, or -1 if the end of the file has been reached.
* @throws AsynchronousCloseException
* if this channel is closed by another thread during this read
* operation.
@@ -413,6 +367,7 @@
* if the channel has not been opened in a mode that permits
* reading.
*/
+ @Override
public final long read(ByteBuffer[] buffers) throws IOException {
return read(buffers, 0, buffers.length);
}
@@ -433,7 +388,7 @@
* the index of the first buffer to store bytes in.
* @param number
* the maximum number of buffers to store bytes in.
- * @return the number of bytes actually read.
+ * @return the number of bytes actually read, or -1 if the end of the file has been reached.
* @throws AsynchronousCloseException
* if this channel is closed by another thread during this read
* operation.
@@ -456,14 +411,9 @@
throws IOException;
/**
- * Returns the size of the file underlying this channel in bytes.
- *
- * @return the size of the file in bytes.
- * @throws ClosedChannelException
- * if this channel is closed.
- * @throws IOException
- * if an I/O error occurs while getting the size of the file.
+ * {@inheritDoc}
*/
+ @Override
public abstract long size() throws IOException;
/**
@@ -541,25 +491,9 @@
WritableByteChannel target) throws IOException;
/**
- * Truncates the file underlying this channel to a given size. Any bytes
- * beyond the given size are removed from the file. If there are no bytes
- * beyond the given size then the file contents are unmodified.
- * <p>
- * If the file position is currently greater than the given size, then it is
- * set to the given size.
- *
- * @param size
- * the maximum size of the underlying file.
- * @throws IllegalArgumentException
- * if the requested size is negative.
- * @throws ClosedChannelException
- * if this channel is closed.
- * @throws NonWritableChannelException
- * if the channel cannot be written to.
- * @throws IOException
- * if another I/O error occurs.
- * @return this channel.
+ * {@inheritDoc}
*/
+ @Override
public abstract FileChannel truncate(long size) throws IOException;
/**
@@ -620,30 +554,9 @@
throws IOException;
/**
- * Writes bytes from the given byte buffer to this file channel.
- * <p>
- * The bytes are written starting at the current file position, and after
- * some number of bytes are written (up to the remaining number of bytes in
- * the buffer) the file position is increased by the number of bytes
- * actually written.
- *
- * @param src
- * the byte buffer containing the bytes to be written.
- * @return the number of bytes actually written.
- * @throws NonWritableChannelException
- * if the channel was not opened for writing.
- * @throws ClosedChannelException
- * if the channel was already closed.
- * @throws AsynchronousCloseException
- * if another thread closes the channel during the write.
- * @throws ClosedByInterruptException
- * if another thread interrupts the calling thread while this
- * operation is in progress. The interrupt state of the calling
- * thread is set and the channel is closed.
- * @throws IOException
- * if another I/O error occurs, details are in the message.
- * @see java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
+ * {@inheritDoc}
*/
+ @Override
public abstract int write(ByteBuffer src) throws IOException;
/**
@@ -712,6 +625,7 @@
* @throws NonWritableChannelException
* if this channel was not opened for writing.
*/
+ @Override
public final long write(ByteBuffer[] buffers) throws IOException {
return write(buffers, 0, buffers.length);
}
@@ -752,6 +666,7 @@
* @throws NonWritableChannelException
* if this channel was not opened for writing.
*/
+ @Override
public abstract long write(ByteBuffer[] buffers, int offset, int length)
throws IOException;
}
diff --git a/luni/src/main/java/java/nio/channels/FileLock.java b/luni/src/main/java/java/nio/channels/FileLock.java
index 360f826..037537c 100644
--- a/luni/src/main/java/java/nio/channels/FileLock.java
+++ b/luni/src/main/java/java/nio/channels/FileLock.java
@@ -108,14 +108,23 @@
/**
* Returns the lock's {@link FileChannel}.
- *
- * @return the channel.
*/
public final FileChannel channel() {
return channel;
}
/**
+ * Returns the {@link Channel} that holds this lock.
+ *
+ * @since 1.7
+ * @hide Until ready for an API update
+ */
+ // TODO: unhiding, add to channel(): This method has been superseded by {@link #acquiredBy()}.
+ public Channel acquiredBy() {
+ return channel;
+ }
+
+ /**
* Returns the lock's starting position in the file.
*
* @return the lock position.
diff --git a/luni/src/main/java/java/nio/channels/MembershipKey.java b/luni/src/main/java/java/nio/channels/MembershipKey.java
new file mode 100644
index 0000000..18ff92d
--- /dev/null
+++ b/luni/src/main/java/java/nio/channels/MembershipKey.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 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.nio.channels;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+
+/**
+ * A token produced as the result of joining a multicast group with
+ * {@link DatagramChannel#join(java.net.InetAddress, java.net.NetworkInterface)} or
+ * {@link DatagramChannel#join(java.net.InetAddress, java.net.NetworkInterface,
+ * java.net.InetAddress)}.
+ *
+ * <p>A multicast group membership can be source-specific or any-source. Source-specific memberships
+ * only allow datagrams from a single source address to be received. Any-source memberships
+ * initially allow datagrams from any source address to be received, but may have individual unicast
+ * IP addresses blocked via {@link #block(java.net.InetAddress)}. Any-source membership keys return
+ * {@code null} from {@link #sourceAddress()}.
+ *
+ * <p>See <a href="http://tools.ietf.org/html/rfc3678">RFC 3678: Socket Interface Extensions for
+ * Multicast Source Filters</a> for concepts and terminology associated with multicast membership.
+ *
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+public abstract class MembershipKey {
+
+ protected MembershipKey() {}
+
+ /**
+ * Returns {@code true} until the membership is dropped with {@link #drop()} or the associated
+ * channel is closed.
+ */
+ public abstract boolean isValid();
+
+ /**
+ * Drops this membership from the multicast group, invalidating this key.
+ */
+ public abstract void drop();
+
+ /**
+ * Blocks datagrams from the specified source address; after this call any datagrams received from
+ * the address will be discarded. Blocking an already-blocked source address has no effect. A
+ * blocked address can be unblocked by calling {@link #unblock(java.net.InetAddress)}.
+ *
+ * <p>The block may not take effect instantaneously: datagrams that are already buffered by the
+ * underlying OS may still be delivered.
+ *
+ * <p>There is an OS-level limit on the number of source addresses that can be block for a given
+ * {@code groupAddress}, {@code networkInterface} pair. This is typically 10. Attempts to add
+ * more than this result in a {@code SocketException}.
+ *
+ * <p>If this membership key is source-specific an {@link IllegalStateException} is thrown.
+ *
+ * @throws IllegalStateException
+ * if this membership key is no longer valid or is of the wrong type
+ * @throws IllegalArgumentException
+ * if the source address is not unicast address of the same type as the multicast group
+ * address supplied when the group was joined
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ public abstract MembershipKey block(InetAddress source) throws IOException;
+
+ /**
+ * Unblocks datagrams from the specified source address that were previously blocked with a call
+ * to {@link #block(java.net.InetAddress)}; after this call any datagrams received from the
+ * address will be received. Unblocking an address that is not currently blocked throws an
+ * {@code IllegalStateException}.
+ *
+ * <p>If this membership key is source-specific an {@link IllegalStateException} is thrown.
+ *
+ * @throws IllegalStateException
+ * if this membership key is no longer valid or is of the wrong type, or the address is
+ * not currently blocked
+ * @throws IllegalArgumentException
+ * if the source address is not unicast address of the same type as the multicast group
+ * address supplied when the group was joined
+ */
+ public abstract MembershipKey unblock(InetAddress source);
+
+ /**
+ * Returns the {@code MulticastChannel} associated with this key. Continues returning the value
+ * even when the key has been invalidated.
+ */
+ public abstract MulticastChannel channel();
+
+ /**
+ * Returns the multicast group address associated with this key. Continues returning the value
+ * even when the key has been invalidated.
+ */
+ public abstract InetAddress group();
+
+ /**
+ * Returns the network interface associated with this key. Continues returning the value
+ * even when the key has been invalidated.
+ */
+ public abstract NetworkInterface networkInterface();
+
+ /**
+ * Returns the source address associated with this key if the membership is source-specific.
+ * Returns {@code null} if the membership is any-source. Continues returning the value
+ * even when the key has been invalidated.
+ */
+ public abstract InetAddress sourceAddress();
+}
diff --git a/luni/src/main/java/java/nio/channels/MulticastChannel.java b/luni/src/main/java/java/nio/channels/MulticastChannel.java
new file mode 100644
index 0000000..41ef501
--- /dev/null
+++ b/luni/src/main/java/java/nio/channels/MulticastChannel.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014 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.nio.channels;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+
+/**
+ * A type of {@link NetworkChannel} that supports IP multicasting. IP multicasting allows for
+ * efficient routing of an IP datagram to multiple hosts. Hosts wishing to receive multicast
+ * datagrams join a multicast group identified by a multicast IP address.
+ *
+ * <p>Any datagram socket can be used to <em>send</em> to a multicast group: senders <em>do not</em>
+ * have to be a member of the group.
+ *
+ * <p>See <a href="http://www.ietf.org/rfc/rfc2236.txt">RFC 2236: Internet Group Management
+ * Protocol, Version 2</a> and <a href="http://www.ietf.org/rfc/rfc3376.txt">RFC 3376: Internet
+ * Group Management Protocol, Version 3</a> for network-level information regarding IPv4 group
+ * membership. See <a href="http://www.ietf.org/rfc/rfc2710.txt">RFC 2710: Multicast Listener
+ * Discovery (MLD) for IPv6</a> and <a href="http://www.ietf.org/rfc/rfc3810.txt">RFC 3810:
+ * Multicast Listener Discovery Version 2 (MLDv2) for IPv6</a> for equivalent IPv6 information.
+ *
+ * <p>See <a href="http://tools.ietf.org/html/rfc3678">RFC 3678: Socket Interface Extensions for
+ * Multicast Source Filters</a> for concepts and terminology associated with multicast membership.
+ *
+ * <p>IP multicast requires support from network infrastructure; networks may not support
+ * all features of IP multicast.
+ *
+ * <p>A channel can be restricted to send multicast datagrams through a specific
+ * {@link NetworkInterface} by using {@link #setOption(java.net.SocketOption, Object)} with
+ * {@link java.net.StandardSocketOptions#IP_MULTICAST_IF}.
+ *
+ * <p>A channel may or may not receive multicast datagrams sent from this host, determined by the
+ * {@link java.net.StandardSocketOptions#IP_MULTICAST_LOOP} option.
+ *
+ * <p>The time-to-live for multicast datagrams can be set using the
+ * {@link java.net.StandardSocketOptions#IP_MULTICAST_TTL} option.
+ *
+ * <p>Usually multicast channels should have {@link java.net.StandardSocketOptions#SO_REUSEADDR}
+ * set to {@code true} before binding to enable multiple sockets on this host to be members of
+ * the same multicast group.
+ *
+ * <p>Typically multicast channels are {@link NetworkChannel#bind bound} to a wildcard address
+ * such as "0.0.0.0" (IPv4) or "::" (IPv6). They may also be bound to a multicast group address.
+ * Binding to a multicast group address restricts datagrams received to only those sent to the
+ * multicast group. When the wildcard address is used the socket may join multiple groups and also
+ * receive non-multicast datagrams sent directly to the host. The port the channel is bound to is
+ * important: only datagrams sent to the group on that port will be received.
+ *
+ * <p>Having bound a channel, the group can be joined. Memberships are either "any-source" or
+ * "source-specific". The type of membership is determined by the variant of {@code join} that is
+ * used. See {@link #join(java.net.InetAddress, java.net.NetworkInterface)} and
+ * {@link #join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress)} for more
+ * information.
+ *
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+public interface MulticastChannel extends NetworkChannel {
+
+ // @hide Until ready for a public API change
+ // /**
+ // * {@inheritDoc}
+ // *
+ // * If the channel is currently part of one or more multicast groups then the memberships are
+ // * dropped and any associated {@code MembershipKey} objects are invalidated.
+ // */
+ void close() throws IOException;
+
+ /**
+ * Creates an any-source membership to the {@code groupAddress} on the specified
+ * {@code networkInterface}. Returns a {@code MembershipKey} that can be used to modify or
+ * {@link MembershipKey#drop()} the membership. See {@link MembershipKey#block(InetAddress)} and
+ * {@link MembershipKey#unblock(InetAddress)} for methods to modify source-address
+ * filtering.
+ *
+ * <p>A channel may join several groups. Each membership is network interface-specific: an
+ * application must join the group for each applicable network interface to receive datagrams.
+ *
+ * <p>Any-source and source-specific memberships cannot be mixed for a group address on a given
+ * network interface. An {@code IllegalStateException} will be thrown if joins of different types
+ * are attempted for a given {@code groupAddress}, {@code networkInterface} pair. Joining a
+ * multicast group with the same arguments as an existing, valid membership returns the same
+ * {@code MembershipKey}.
+ *
+ * <p>There is an OS-level limit on the number of multicast groups a process can join.
+ * This is typically 20. Attempts to join more than this result in a {@code SocketException}.
+ *
+ * @param groupAddress the multicast group address to join
+ * @param networkInterface the network address to join with
+ * @throws IllegalArgumentException
+ * if the group address is not a multicast address or the network interface does not
+ * support multicast
+ * @throws IllegalStateException
+ * if the channel already has a source-specific membership for the group/network interface
+ * @throws ClosedChannelException
+ * if the channel is closed
+ * @throws IOException
+ * if some other I/O error occurs
+ * @hide Until ready for a public API change
+ */
+ MembershipKey join(InetAddress groupAddress, NetworkInterface networkInterface)
+ throws IOException;
+
+ /**
+ * Creates a source-specific membership to the {@code groupAddress} on the specified
+ * {@code networkInterface} filtered by the {@code sourceAddress}. Returns a
+ * {@code MembershipKey} that can be used to {@link MembershipKey#drop()} the membership.
+ *
+ * <p>A channel may join several groups. Each membership is network interface-specific: an
+ * application must join the group for each applicable network interface to receive datagrams.
+ *
+ * <p>Any-source and source-specific memberships cannot be mixed for a group address on a given
+ * network interface. An {@code IllegalStateException} will be thrown if joins of different types
+ * are attempted for a given {@code groupAddress}, {@code networkInterface} pair. Joining a
+ * multicast group with the same arguments as an existing, valid membership returns the same
+ * {@code MembershipKey}.
+ *
+ * <p>There is an OS-level limit on the number of multicast groups a process can join.
+ * This is typically 20. Attempts to join more than this result in a {@code SocketException}.
+ *
+ * <p>There is an OS-level limit on the number of source addresses that can be joined for a given
+ * {@code groupAddress}, {@code networkInterface} pair. This is typically 10. Attempts to add
+ * more than this result in a {@code SocketException}.
+ *
+ * @param groupAddress the multicast group address to join
+ * @param networkInterface the network address to join with
+ * @param sourceAddress the source address to restrict datagrams to
+ * @throws IllegalArgumentException
+ * if the group address is not a multicast address, the network interface does not
+ * support multicast, or the {@code groupAddress} and {@code sourceAddress} are not of
+ * compatible types
+ * @throws IllegalStateException
+ * if the channel already has a source-specific membership for the group/network interface
+ * @throws ClosedChannelException
+ * if the channel is closed
+ * @throws IOException
+ * if some other I/O error occurs
+ * @hide Until ready for a public API change
+ */
+ MembershipKey join(
+ InetAddress groupAddress, NetworkInterface networkInterface, InetAddress sourceAddress)
+ throws IOException;
+
+}
diff --git a/luni/src/main/java/java/nio/channels/NetworkChannel.java b/luni/src/main/java/java/nio/channels/NetworkChannel.java
new file mode 100644
index 0000000..9b46e30
--- /dev/null
+++ b/luni/src/main/java/java/nio/channels/NetworkChannel.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 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.nio.channels;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.net.SocketOption;
+import java.util.Set;
+
+/**
+ * A common interface for channels that are backed by network sockets.
+ *
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+public interface NetworkChannel extends AutoCloseable, Channel, Closeable {
+
+ /**
+ * Binds this channel to the given local socket address. If the {@code localAddr} is set
+ * to {@code null} the socket will be bound to an available local address on any free port of
+ * the system.
+ *
+ * @param local
+ * the local machine address and port to bind on.
+ * @return this channel.
+ * @throws UnsupportedAddressTypeException
+ * if the {@code SocketAddress} is not supported.
+ * @throws ClosedChannelException
+ * if the channel is closed.
+ * @throws AlreadyBoundException
+ * if the channel is already bound.
+ * @throws IOException
+ * if another I/O error occurs.
+ * @hide Until ready for a public API change
+ */
+ NetworkChannel bind(SocketAddress local) throws IOException;
+
+ /**
+ * Returns the local socket address the channel is bound to. The socket may be bound explicitly
+ * via {@link #bind(java.net.SocketAddress)} or similar methods, or as a side-effect when other
+ * methods are called, depending on the implementation. If the channel is not bound {@code null}
+ * is returned.
+ *
+ * <p>If IP is being used, the returned object will be a subclass of
+ * {@link java.net.InetSocketAddress}
+ *
+ * @return the local socket address, or {@code null} if the socket is not bound
+ * @throws ClosedChannelException
+ * if the channel is closed.
+ * @throws IOException
+ * if another I/O error occurs.
+ * @hide Until ready for a public API change
+ */
+ SocketAddress getLocalAddress() throws IOException;
+
+ /**
+ * Returns the value for the socket option.
+ *
+ * @throws UnsupportedOperationException
+ * if the option is not supported by the socket.
+ * @throws java.nio.channels.ClosedChannelException
+ * if the socket is closed
+ * @throws IOException
+ * if the value cannot be read.
+ * @hide Until ready for a public API change
+ * @see java.net.StandardSocketOptions
+ */
+ <T> T getOption(SocketOption<T> option) throws IOException;
+
+ /**
+ * Sets the value for the socket option.
+ *
+ * @return this NetworkChannel
+ * @throws UnsupportedOperationException
+ * if the option is not supported by the socket.
+ * @throws IllegalArgumentException
+ * if the value is not valid for the option.
+ * @throws java.nio.channels.ClosedChannelException
+ * if the socket is closed
+ * @throws IOException
+ * if the value cannot be written.
+ * @hide Until ready for a public API change
+ * @see java.net.StandardSocketOptions
+ */
+ <T> NetworkChannel setOption(SocketOption<T> option, T value) throws IOException;
+
+ /**
+ * Returns the set of socket options supported by this channel.
+ *
+ * @hide Until ready for a public API change
+ */
+ Set<SocketOption<?>> supportedOptions();
+}
diff --git a/luni/src/main/java/java/nio/channels/SeekableByteChannel.java b/luni/src/main/java/java/nio/channels/SeekableByteChannel.java
new file mode 100644
index 0000000..f4d6beb
--- /dev/null
+++ b/luni/src/main/java/java/nio/channels/SeekableByteChannel.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 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.nio.channels;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * An interface for channels that keep a pointer to a current position within an underlying
+ * byte-based data source such as a file.
+ *
+ * <p>SeekableByteChannels have a pointer into the underlying data source which is referred to as a
+ * <em>position</em>. The position can be manipulated by moving it within the data source, and the
+ * current position can be queried.
+ *
+ * <p>SeekableByteChannels also have an associated <em>size</em>. The size of the channel is the
+ * number of bytes that the data source currently contains. The size of the data source can be
+ * manipulated by adding more bytes to the end or by removing bytes from the end. See
+ * {@link #truncate}, {@link #position} and {@link #write} for details. The current size can also
+ * be queried.
+ *
+ * @hide Until ready for a public API change
+ * @since 1.7
+ */
+public interface SeekableByteChannel extends ByteChannel {
+
+ /**
+ * Returns the current position as a positive number of bytes from the start of the underlying
+ * data source.
+ *
+ * @throws ClosedChannelException
+ * if this channel is closed.
+ * @throws IOException
+ * if another I/O error occurs.
+ */
+ long position() throws IOException;
+
+ /**
+ * Sets the channel's position to {@code newPosition}.
+ *
+ * <p>The argument is the number of bytes counted from the start of the data source. The position
+ * cannot be set to a value that is negative. The new position can be set beyond the current
+ * size. If set beyond the current size, attempts to read will return end-of-file. Write
+ * operations will succeed but they will fill the bytes between the current end of the data
+ * source
+ * and the new position with the required number of (unspecified) byte values.
+ *
+ * @return the channel.
+ * @throws IllegalArgumentException
+ * if the new position is negative.
+ * @throws ClosedChannelException
+ * if this channel is closed.
+ * @throws IOException
+ * if another I/O error occurs.
+ */
+ SeekableByteChannel position(long newPosition) throws IOException;
+
+ /**
+ * Returns the size of the data source underlying this channel in bytes.
+ *
+ * @throws ClosedChannelException
+ * if this channel is closed.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ long size() throws IOException;
+
+ /**
+ * Truncates the data source underlying this channel to a given size. Any bytes beyond the given
+ * size are removed. If there are no bytes beyond the given size then the contents are
+ * unmodified.
+ *
+ * <p>If the position is currently greater than the given size, then it is set to the new size.
+ *
+ * @return this channel.
+ * @throws IllegalArgumentException
+ * if the requested size is negative.
+ * @throws ClosedChannelException
+ * if this channel is closed.
+ * @throws NonWritableChannelException
+ * if the channel cannot be written to.
+ * @throws IOException
+ * if another I/O error occurs.
+ */
+ SeekableByteChannel truncate(long size) throws IOException;
+
+ /**
+ * Writes bytes from the given byte buffer to this channel.
+ *
+ * <p>The bytes are written starting at the channel's current position, and after some number of
+ * bytes are written (up to the {@link java.nio.Buffer#remaining() remaining} number of bytes in
+ * the buffer) the channel's position is increased by the number of bytes actually written.
+ *
+ * <p>If the channel's position is beyond the current end of the underlying data source, then the
+ * data source is first extended up to the given position by the required number of unspecified
+ * byte values.
+ *
+ * @param buffer
+ * the byte buffer containing the bytes to be written.
+ * @return the number of bytes actually written.
+ * @throws NonWritableChannelException
+ * if the channel was not opened for writing.
+ * @throws ClosedChannelException
+ * if the channel was already closed.
+ * @throws AsynchronousCloseException
+ * if another thread closes the channel during the write.
+ * @throws ClosedByInterruptException
+ * if another thread interrupts the calling thread while this operation is in progress. The
+ * interrupt state of the calling thread is set and the channel is closed.
+ * @throws IOException
+ * if another I/O error occurs, details are in the message.
+ */
+ @Override
+ int write(ByteBuffer buffer) throws IOException;
+
+ /**
+ * Reads bytes from this channel into the given buffer.
+ *
+ * <p>If the channels position is beyond the current end of the underlying data source then
+ * end-of-file (-1) is returned.
+ *
+ * <p>The bytes are read starting at the channel's current position, and after some number of
+ * bytes are read (up to the {@link java.nio.Buffer#remaining() remaining} number of bytes in the
+ * buffer) the channel's position is increased by the number of bytes actually read. The bytes
+ * will be read into the buffer starting at the buffer's current
+ * {@link java.nio.Buffer#position() position}. The buffer's {@link java.nio.Buffer#limit()
+ * limit} is not changed.
+ *
+ * <p>The call may block if other threads are also attempting to read from the same channel.
+ *
+ * @param buffer
+ * the byte buffer to receive the bytes.
+ * @return the number of bytes actually read, or -1 if the end of the data has been reached
+ * @throws AsynchronousCloseException
+ * if another thread closes the channel during the read.
+ * @throws ClosedByInterruptException
+ * if another thread interrupts the calling thread while the operation is in progress. The
+ * interrupt state of the calling thread is set and the channel is closed.
+ * @throws ClosedChannelException
+ * if the channel is closed.
+ * @throws IOException
+ * another I/O error occurs, details are in the message.
+ * @throws NonReadableChannelException
+ * if the channel was not opened for reading.
+ */
+ @Override
+ int read(ByteBuffer buffer) throws IOException;
+
+}
diff --git a/luni/src/main/java/java/nio/channels/ServerSocketChannel.java b/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
index f3c3390..c720451 100644
--- a/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
+++ b/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
@@ -19,8 +19,11 @@
import java.io.IOException;
import java.net.ServerSocket;
+import java.net.SocketAddress;
+import java.net.SocketOption;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
+import java.util.Set;
/**
* A {@code ServerSocketChannel} is a partial abstraction of a selectable,
@@ -34,7 +37,8 @@
* {@link NotYetBoundException}. It can be bound by calling the bind method of a
* related {@code ServerSocket} instance.
*/
-public abstract class ServerSocketChannel extends AbstractSelectableChannel {
+public abstract class ServerSocketChannel extends AbstractSelectableChannel
+ implements NetworkChannel {
/**
* Constructs a new {@link ServerSocketChannel}.
@@ -82,6 +86,76 @@
public abstract ServerSocket socket();
/**
+ * {@inheritDoc}
+ *
+ * <p>This is equivalent to {@code bind(local, 0)}.
+ * @hide Until ready for a public API change
+ */
+ @Override
+ public final ServerSocketChannel bind(SocketAddress local) throws IOException {
+ return bind(local, 0);
+ }
+
+ /**
+ * Binds this server channel to the given local socket address. If the {@code localAddr} is set
+ * to {@code null} the socket will be bound to an available local address on any free port of
+ * the system.
+ *
+ * @param localAddr
+ * the local machine address and port to bind on.
+ * @param backlog the maximum number of unaccepted connections. Passing 0 or
+ * a negative value yields the default backlog of 50.
+ * @return this {@code ServerSocketChannel}.
+ * @throws UnsupportedAddressTypeException
+ * if the {@code SocketAddress} is not supported.
+ * @throws ClosedChannelException
+ * if the channel is closed.
+ * @throws AlreadyBoundException
+ * if the channel is already bound.
+ * @throws IOException
+ * if another I/O error occurs.
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+ public ServerSocketChannel bind(SocketAddress localAddr, int backlog) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public SocketAddress getLocalAddress() throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> ServerSocketChannel setOption(SocketOption<T> option, T value) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* Accepts a connection to this server-socket channel.
* <p>
* This method returns {@code null} when this channel is non-blocking and no
diff --git a/luni/src/main/java/java/nio/channels/SocketChannel.java b/luni/src/main/java/java/nio/channels/SocketChannel.java
index da5b7ea..a6d1551 100644
--- a/luni/src/main/java/java/nio/channels/SocketChannel.java
+++ b/luni/src/main/java/java/nio/channels/SocketChannel.java
@@ -20,9 +20,11 @@
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
+import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
+import java.util.Set;
/**
* A {@code SocketChannel} is a selectable channel that provides a partial
@@ -62,7 +64,7 @@
* processing, calls to {@link #read} and {@link #write} will block.
*/
public abstract class SocketChannel extends AbstractSelectableChannel implements
- ByteChannel, ScatteringByteChannel, GatheringByteChannel {
+ ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel {
/**
* Constructs a new {@code SocketChannel}.
@@ -140,6 +142,46 @@
*/
public abstract Socket socket();
+ /** @hide Until ready for a public API change */
+ @Override
+ public SocketChannel bind(SocketAddress local) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public SocketAddress getLocalAddress() throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> SocketChannel setOption(SocketOption<T> option, T value) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
/**
* Indicates whether this channel's socket is connected.
*
diff --git a/luni/src/main/java/java/security/AlgorithmParameterGenerator.java b/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
index 61548d7..167204c 100644
--- a/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
+++ b/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
@@ -129,7 +129,8 @@
/**
* Returns a new instance of {@code AlgorithmParameterGenerator} from the
- * specified provider for the specified algorithm.
+ * specified provider for the specified algorithm. The {@code provider}
+ * supplied does not have to be registered.
*
* @param algorithm
* the name of the algorithm to use.
diff --git a/luni/src/main/java/java/security/AlgorithmParameters.java b/luni/src/main/java/java/security/AlgorithmParameters.java
index 073460e..27af018 100644
--- a/luni/src/main/java/java/security/AlgorithmParameters.java
+++ b/luni/src/main/java/java/security/AlgorithmParameters.java
@@ -131,7 +131,8 @@
/**
* Returns a new instance of {@code AlgorithmParameters} from the specified
- * provider for the specified algorithm.
+ * provider for the specified algorithm. The {@code provider} supplied does
+ * not have to be registered.
*
* @param algorithm
* the name of the algorithm to use.
diff --git a/luni/src/main/java/java/security/KeyFactory.java b/luni/src/main/java/java/security/KeyFactory.java
index 3bd05b9..c22212f 100644
--- a/luni/src/main/java/java/security/KeyFactory.java
+++ b/luni/src/main/java/java/security/KeyFactory.java
@@ -112,7 +112,8 @@
/**
* Returns a new instance of {@code KeyFactory} that utilizes the specified
- * algorithm from the specified provider.
+ * algorithm from the specified provider. The {@code provider} supplied
+ * does not have to be registered.
*
* @param algorithm
* the name of the algorithm.
diff --git a/luni/src/main/java/java/security/KeyPairGenerator.java b/luni/src/main/java/java/security/KeyPairGenerator.java
index 8a713d0..d05e902 100644
--- a/luni/src/main/java/java/security/KeyPairGenerator.java
+++ b/luni/src/main/java/java/security/KeyPairGenerator.java
@@ -124,7 +124,8 @@
/**
* Returns a new instance of {@code KeyPairGenerator} that utilizes the
- * specified algorithm from the specified provider.
+ * specified algorithm from the specified provider. The {@code provider}
+ * supplied does not have to be registered.
*
* @param algorithm
* the name of the algorithm to use
diff --git a/luni/src/main/java/java/security/KeyStore.java b/luni/src/main/java/java/security/KeyStore.java
index b0e4945..0611e59 100644
--- a/luni/src/main/java/java/security/KeyStore.java
+++ b/luni/src/main/java/java/security/KeyStore.java
@@ -159,7 +159,8 @@
/**
* Returns a new instance of {@code KeyStore} from the specified provider
- * with the given type.
+ * with the given type. The {@code provider} supplied does not have to be
+ * registered.
*
* @param type
* the type of the returned {@code KeyStore}.
diff --git a/luni/src/main/java/java/security/MessageDigest.java b/luni/src/main/java/java/security/MessageDigest.java
index 9ce376a..70202ac 100644
--- a/luni/src/main/java/java/security/MessageDigest.java
+++ b/luni/src/main/java/java/security/MessageDigest.java
@@ -132,7 +132,8 @@
/**
* Returns a new instance of {@code MessageDigest} that utilizes the
- * specified algorithm from the specified provider.
+ * specified algorithm from the specified provider. The
+ * {@code provider} supplied does not have to be registered.
*
* @param algorithm
* the name of the algorithm to use
diff --git a/luni/src/main/java/java/security/SecureRandom.java b/luni/src/main/java/java/security/SecureRandom.java
index 281885b..df95fa2 100644
--- a/luni/src/main/java/java/security/SecureRandom.java
+++ b/luni/src/main/java/java/security/SecureRandom.java
@@ -190,7 +190,8 @@
/**
* Returns a new instance of {@code SecureRandom} that utilizes the
- * specified algorithm from the specified provider.
+ * specified algorithm from the specified provider. The
+ * {@code provider} supplied does not have to be registered.
*
* @param algorithm
* the name of the algorithm to use.
diff --git a/luni/src/main/java/java/security/Security.java b/luni/src/main/java/java/security/Security.java
index 1228b9e..81bafbd 100644
--- a/luni/src/main/java/java/security/Security.java
+++ b/luni/src/main/java/java/security/Security.java
@@ -73,10 +73,9 @@
// Register default providers
private static void registerDefaultProviders() {
secprops.put("security.provider.1", "com.android.org.conscrypt.OpenSSLProvider");
- secprops.put("security.provider.2", "org.apache.harmony.security.provider.cert.DRLCertFactory");
- secprops.put("security.provider.3", "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider");
- secprops.put("security.provider.4", "org.apache.harmony.security.provider.crypto.CryptoProvider");
- secprops.put("security.provider.5", "com.android.org.conscrypt.JSSEProvider");
+ secprops.put("security.provider.2", "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider");
+ secprops.put("security.provider.3", "org.apache.harmony.security.provider.crypto.CryptoProvider");
+ secprops.put("security.provider.4", "com.android.org.conscrypt.JSSEProvider");
}
/**
diff --git a/luni/src/main/java/java/security/Signature.java b/luni/src/main/java/java/security/Signature.java
index 2d996b9..24f5298 100644
--- a/luni/src/main/java/java/security/Signature.java
+++ b/luni/src/main/java/java/security/Signature.java
@@ -21,10 +21,11 @@
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import org.apache.harmony.security.fortress.Engine;
-
+import org.apache.harmony.security.fortress.Engine.SpiAndProvider;
/**
* {@code Signature} is an engine class which is capable of creating and
@@ -39,13 +40,13 @@
private static final String SERVICE = "Signature";
// Used to access common engine functionality
- private static Engine ENGINE = new Engine(SERVICE);
+ private static final Engine ENGINE = new Engine(SERVICE);
// The provider
- private Provider provider;
+ Provider provider;
// The algorithm.
- private String algorithm;
+ final String algorithm;
/**
* Constant that indicates that this {@code Signature} instance has not yet
@@ -101,16 +102,7 @@
if (algorithm == null) {
throw new NullPointerException("algorithm == null");
}
- Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
- Object spi = sap.spi;
- Provider provider = sap.provider;
- if (spi instanceof Signature) {
- Signature result = (Signature) spi;
- result.algorithm = algorithm;
- result.provider = provider;
- return result;
- }
- return new SignatureImpl((SignatureSpi) spi, provider, algorithm);
+ return getSignature(algorithm, null);
}
/**
@@ -143,12 +135,13 @@
if (p == null) {
throw new NoSuchProviderException(provider);
}
- return getSignatureInstance(algorithm, p);
+ return getSignature(algorithm, p);
}
/**
* Returns a new instance of {@code Signature} that utilizes the specified
- * algorithm from the specified provider.
+ * algorithm from the specified provider. The {@code provider} supplied
+ * does not have to be registered.
*
* @param algorithm
* the name of the algorithm to use.
@@ -170,19 +163,68 @@
if (provider == null) {
throw new IllegalArgumentException("provider == null");
}
- return getSignatureInstance(algorithm, provider);
+ return getSignature(algorithm, provider);
}
- private static Signature getSignatureInstance(String algorithm,
- Provider provider) throws NoSuchAlgorithmException {
- Object spi = ENGINE.getInstance(algorithm, provider, null);
- if (spi instanceof Signature) {
- Signature result = (Signature) spi;
- result.algorithm = algorithm;
- result.provider = provider;
- return result;
+ private static Signature getSignature(String algorithm, Provider provider)
+ throws NoSuchAlgorithmException {
+ if (algorithm == null || algorithm.isEmpty()) {
+ throw new NoSuchAlgorithmException("Unknown algorithm: " + algorithm);
}
- return new SignatureImpl((SignatureSpi) spi, provider, algorithm);
+
+ SpiAndProvider spiAndProvider = tryAlgorithm(null, provider, algorithm);
+ if (spiAndProvider == null) {
+ if (provider == null) {
+ throw new NoSuchAlgorithmException("No provider found for " + algorithm);
+ } else {
+ throw new NoSuchAlgorithmException("Provider " + provider.getName()
+ + " does not provide " + algorithm);
+ }
+ }
+ if (spiAndProvider.spi instanceof Signature) {
+ return (Signature) spiAndProvider.spi;
+ }
+ return new SignatureImpl(algorithm, provider);
+ }
+
+ private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
+ if (provider != null) {
+ Provider.Service service = provider.getService(SERVICE, algorithm);
+ if (service == null) {
+ return null;
+ }
+ return tryAlgorithmWithProvider(key, service);
+ }
+ ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
+ if (services == null) {
+ return null;
+ }
+ for (Provider.Service service : services) {
+ Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
+ if (sap != null) {
+ return sap;
+ }
+ }
+ return null;
+ }
+
+ private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
+ try {
+ if (key != null && !service.supportsParameter(key)) {
+ return null;
+ }
+
+ Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
+ if (sap.spi == null || sap.provider == null) {
+ return null;
+ }
+ if (!(sap.spi instanceof SignatureSpi)) {
+ return null;
+ }
+ return sap;
+ } catch (NoSuchAlgorithmException ignored) {
+ }
+ return null;
}
/**
@@ -191,10 +233,18 @@
* @return the provider associated with this {@code Signature}.
*/
public final Provider getProvider() {
+ ensureProviderChosen();
return provider;
}
/**
+ * This makes sure the provider is chosen since Signature is abstract and
+ * getProvider is final but we need to support late binding.
+ */
+ void ensureProviderChosen() {
+ }
+
+ /**
* Returns the name of the algorithm of this {@code Signature}.
*
* @return the name of the algorithm of this {@code Signature}.
@@ -237,10 +287,10 @@
public final void initVerify(Certificate certificate)
throws InvalidKeyException {
if (certificate instanceof X509Certificate) {
- Set ce = ((X509Certificate) certificate).getCriticalExtensionOIDs();
+ Set<String> ce = ((X509Certificate) certificate).getCriticalExtensionOIDs();
boolean critical = false;
if (ce != null && !ce.isEmpty()) {
- for (Iterator i = ce.iterator(); i.hasNext();) {
+ for (Iterator<String> i = ce.iterator(); i.hasNext();) {
if ("2.5.29.15".equals(i.next())) {
//KeyUsage OID = 2.5.29.15
critical = true;
@@ -583,79 +633,117 @@
}
/**
- *
* Internal Signature implementation
- *
*/
private static class SignatureImpl extends Signature {
- private final SignatureSpi spiImpl;
+ /**
+ * Lock held while the SPI is initializing.
+ */
+ private final Object initLock = new Object();
- public SignatureImpl(SignatureSpi signatureSpi, Provider provider,
- String algorithm) {
+ // The provider specified when creating this instance.
+ private final Provider specifiedProvider;
+
+ private SignatureSpi spiImpl;
+
+ public SignatureImpl(String algorithm, Provider provider) {
super(algorithm);
- super.provider = provider;
- spiImpl = signatureSpi;
+ this.specifiedProvider = provider;
+ }
+
+ private SignatureImpl(String algorithm, Provider provider, SignatureSpi spi) {
+ this(algorithm, provider);
+ spiImpl = spi;
+ }
+
+ @Override
+ void ensureProviderChosen() {
+ getSpi(null);
}
@Override
protected byte[] engineSign() throws SignatureException {
- return spiImpl.engineSign();
+ return getSpi().engineSign();
}
@Override
protected void engineUpdate(byte arg0) throws SignatureException {
- spiImpl.engineUpdate(arg0);
+ getSpi().engineUpdate(arg0);
}
@Override
protected boolean engineVerify(byte[] arg0) throws SignatureException {
- return spiImpl.engineVerify(arg0);
+ return getSpi().engineVerify(arg0);
}
@Override
- protected void engineUpdate(byte[] arg0, int arg1, int arg2)
- throws SignatureException {
- spiImpl.engineUpdate(arg0, arg1, arg2);
+ protected void engineUpdate(byte[] arg0, int arg1, int arg2) throws SignatureException {
+ getSpi().engineUpdate(arg0, arg1, arg2);
}
@Override
- protected void engineInitSign(PrivateKey arg0)
- throws InvalidKeyException {
- spiImpl.engineInitSign(arg0);
+ protected void engineInitSign(PrivateKey arg0) throws InvalidKeyException {
+ getSpi(arg0).engineInitSign(arg0);
}
@Override
- protected void engineInitVerify(PublicKey arg0)
- throws InvalidKeyException {
- spiImpl.engineInitVerify(arg0);
+ protected void engineInitVerify(PublicKey arg0) throws InvalidKeyException {
+ getSpi(arg0).engineInitVerify(arg0);
}
@Override
- protected Object engineGetParameter(String arg0)
- throws InvalidParameterException {
- return spiImpl.engineGetParameter(arg0);
+ protected Object engineGetParameter(String arg0) throws InvalidParameterException {
+ return getSpi().engineGetParameter(arg0);
}
@Override
protected void engineSetParameter(String arg0, Object arg1)
throws InvalidParameterException {
- spiImpl.engineSetParameter(arg0, arg1);
+ getSpi().engineSetParameter(arg0, arg1);
}
@Override
protected void engineSetParameter(AlgorithmParameterSpec arg0)
throws InvalidAlgorithmParameterException {
- spiImpl.engineSetParameter(arg0);
+ getSpi().engineSetParameter(arg0);
}
@Override
public Object clone() throws CloneNotSupportedException {
if (spiImpl instanceof Cloneable) {
SignatureSpi spi = (SignatureSpi) spiImpl.clone();
- return new SignatureImpl(spi, getProvider(), getAlgorithm());
+ return new SignatureImpl(getAlgorithm(), getProvider(), spiImpl);
}
throw new CloneNotSupportedException();
}
+
+ /**
+ * Makes sure a CipherSpi that matches this type is selected.
+ */
+ private SignatureSpi getSpi(Key key) {
+ synchronized (initLock) {
+ if (spiImpl != null && key == null) {
+ return spiImpl;
+ }
+
+ final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
+ if (sap == null) {
+ throw new ProviderException("No provider for " + getAlgorithm());
+ }
+
+ spiImpl = (SignatureSpi) sap.spi;
+ provider = sap.provider;
+
+ return spiImpl;
+ }
+ }
+
+ /**
+ * Convenience call when the Key is not available.
+ */
+ private SignatureSpi getSpi() {
+ return getSpi(null);
+ }
}
}
diff --git a/luni/src/main/java/java/security/cert/CertPathValidator.java b/luni/src/main/java/java/security/cert/CertPathValidator.java
index a3a666a..962f530 100644
--- a/luni/src/main/java/java/security/cert/CertPathValidator.java
+++ b/luni/src/main/java/java/security/cert/CertPathValidator.java
@@ -140,7 +140,8 @@
/**
* Returns a new certification path validator for the specified algorithm
- * from the specified provider.
+ * from the specified provider. The {@code provider} supplied does not
+ * have to be registered.
*
* @param algorithm
* the algorithm name.
diff --git a/luni/src/main/java/java/security/cert/CertStore.java b/luni/src/main/java/java/security/cert/CertStore.java
index 72d356f..606c93b 100644
--- a/luni/src/main/java/java/security/cert/CertStore.java
+++ b/luni/src/main/java/java/security/cert/CertStore.java
@@ -151,7 +151,9 @@
/**
* Creates a new {@code CertStore} instance from the specified provider with
- * the specified type and initialized with the specified parameters.
+ * the specified type and initialized with the specified parameters. The
+ * {@code provider} supplied does not have to be registered.
+ *
* @param type
* the certificate store type.
* @param params
diff --git a/luni/src/main/java/java/security/cert/CertificateFactory.java b/luni/src/main/java/java/security/cert/CertificateFactory.java
index d537488..2805c0e 100644
--- a/luni/src/main/java/java/security/cert/CertificateFactory.java
+++ b/luni/src/main/java/java/security/cert/CertificateFactory.java
@@ -128,7 +128,8 @@
/**
* Creates a new {@code CertificateFactory} instance from the specified
- * provider that provides the requested certificate type.
+ * provider that provides the requested certificate type. The
+ * {@code provider} supplied does not have to be registered.
*
* @param type
* the certificate type.
diff --git a/luni/src/main/java/java/security/security.properties b/luni/src/main/java/java/security/security.properties
index dd5830d..a06283b 100644
--- a/luni/src/main/java/java/security/security.properties
+++ b/luni/src/main/java/java/security/security.properties
@@ -20,13 +20,11 @@
#
# Android's provider of OpenSSL backed implementations
security.provider.1=com.android.org.conscrypt.OpenSSLProvider
-# Favor Harmony's CertificateFactory.X509 over BouncyCastle's
-security.provider.2=org.apache.harmony.security.provider.cert.DRLCertFactory
# Android's stripped down BouncyCastle provider
-security.provider.3=com.android.org.bouncycastle.jce.provider.BouncyCastleProvider
+security.provider.2=com.android.org.bouncycastle.jce.provider.BouncyCastleProvider
# Remaining Harmony providers
-security.provider.4=org.apache.harmony.security.provider.crypto.CryptoProvider
-security.provider.5=com.android.org.conscrypt.JSSEProvider
+security.provider.3=org.apache.harmony.security.provider.crypto.CryptoProvider
+security.provider.4=com.android.org.conscrypt.JSSEProvider
diff --git a/luni/src/main/java/java/sql/Date.java b/luni/src/main/java/java/sql/Date.java
index 4aac326..206d885 100644
--- a/luni/src/main/java/java/sql/Date.java
+++ b/luni/src/main/java/java/sql/Date.java
@@ -214,23 +214,19 @@
if (dateString == null) {
throw new IllegalArgumentException("dateString == null");
}
- int firstIndex = dateString.indexOf('-');
- int secondIndex = dateString.indexOf('-', firstIndex + 1);
- // secondIndex == -1 means none or only one separator '-' has been
- // found.
- // The string is separated into three parts by two separator characters,
- // if the first or the third part is null string, we should throw
- // IllegalArgumentException to follow RI
- if (secondIndex == -1 || firstIndex == 0
- || secondIndex + 1 == dateString.length()) {
+ if (dateString.length() > 10) {
+ // early fail to avoid parsing huge invalid strings
throw new IllegalArgumentException();
}
- // parse each part of the string
- int year = Integer.parseInt(dateString.substring(0, firstIndex));
- int month = Integer.parseInt(dateString.substring(firstIndex + 1,
- secondIndex));
- int day = Integer.parseInt(dateString.substring(secondIndex + 1,
- dateString.length()));
+
+ String[] parts = dateString.split("-");
+ if (parts.length != 3) {
+ throw new IllegalArgumentException();
+ }
+
+ int year = Integer.parsePositiveInt(parts[0]);
+ int month = Integer.parsePositiveInt(parts[1]);
+ int day = Integer.parsePositiveInt(parts[2]);
return new Date(year - 1900, month - 1, day);
}
diff --git a/luni/src/main/java/java/sql/Timestamp.java b/luni/src/main/java/java/sql/Timestamp.java
index f35fa9b..a45a2b8 100644
--- a/luni/src/main/java/java/sql/Timestamp.java
+++ b/luni/src/main/java/java/sql/Timestamp.java
@@ -408,7 +408,7 @@
throw new IllegalArgumentException("Argument cannot be null");
}
- // omit trailing whitespace
+ // Omit trailing whitespace
s = s.trim();
if (!Pattern.matches(TIME_FORMAT_REGEX, s)) {
throw badTimestampString(s);
@@ -424,14 +424,14 @@
* with the ParsePosition indicating the index of the "." which should
* precede the nanoseconds value
*/
- Date theDate;
+ Date date;
try {
- theDate = df.parse(s, pp);
+ date = df.parse(s, pp);
} catch (Exception e) {
throw badTimestampString(s);
}
- if (theDate == null) {
+ if (date == null) {
throw badTimestampString(s);
}
@@ -445,66 +445,38 @@
*/
int position = pp.getIndex();
int remaining = s.length() - position;
- int theNanos;
+ int nanos;
if (remaining == 0) {
// First, allow for the case where no fraction of a second is given:
- theNanos = 0;
+ nanos = 0;
} else {
- /*
- * Case where fraction of a second is specified: Require 1 character
- * plus the "." in the remaining part of the string...
- */
- if ((s.length() - position) < ".n".length()) {
+ // Validate the string is in the range ".0" to ".999999999"
+ if (remaining < 2 || remaining > 10 || s.charAt(position) != '.') {
throw badTimestampString(s);
}
-
- /*
- * If we're strict, we should not allow any EXTRA characters after
- * the 9 digits
- */
- if ((s.length() - position) > ".nnnnnnnnn".length()) {
- throw badTimestampString(s);
- }
-
- // Require the next character to be a "."
- if (s.charAt(position) != '.') {
- throw new NumberFormatException("Bad input string format: expected '.' not '" +
- s.charAt(position) + "' in \"" + s + "\"");
- }
- // Get the length of the number string - need to account for the '.'
- int nanoLength = s.length() - position - 1;
-
- // Get the 9 characters following the "." as an integer
- String theNanoString = s.substring(position + 1, position + 1
- + nanoLength);
- /*
- * We must adjust for the cases where the nanos String was not 9
- * characters long by padding out with zeros
- */
- theNanoString = theNanoString + "000000000";
- theNanoString = theNanoString.substring(0, 9);
-
try {
- theNanos = Integer.parseInt(theNanoString);
- } catch (Exception e) {
- // If we get here, the string was not a number
+ nanos = Integer.parsePositiveInt(s.substring(position + 1));
+ } catch (NumberFormatException e) {
throw badTimestampString(s);
}
+ // We must adjust for the cases where the nanos String was not 9
+ // characters long (i.e. ".123" means 123000000 nanos)
+ if (nanos != 0) {
+ for (int i = remaining - 1; i < 9; i++) {
+ nanos *= 10;
+ }
+ }
}
- if (theNanos < 0 || theNanos > 999999999) {
- throw badTimestampString(s);
- }
+ Timestamp timestamp = new Timestamp(date.getTime());
+ timestamp.setNanos(nanos);
- Timestamp theTimestamp = new Timestamp(theDate.getTime());
- theTimestamp.setNanos(theNanos);
-
- return theTimestamp;
+ return timestamp;
}
private static IllegalArgumentException badTimestampString(String s) {
- throw new IllegalArgumentException("Timestamp format must be " +
+ return new IllegalArgumentException("Timestamp format must be " +
"yyyy-MM-dd HH:mm:ss.fffffffff; was '" + s + "'");
}
}
diff --git a/luni/src/main/java/java/text/CollationElementIterator.java b/luni/src/main/java/java/text/CollationElementIterator.java
index 5da3b65..4f75a9a 100644
--- a/luni/src/main/java/java/text/CollationElementIterator.java
+++ b/luni/src/main/java/java/text/CollationElementIterator.java
@@ -43,6 +43,11 @@
* "\u0086b": the first collation element is collation_element('a'), the
* second one is collation_element('e'), and the third collation element is
* collation_element('b').
+ *
+ * <p>Note that calls to {@code next} and {@code previous} can not be mixed.
+ * To change iteration direction, {@code reset}, {@code setOffset} or {@code setText}
+ * must be called to reset the iterator. If a change of direction is done without one
+ * of these calls, the result is undefined.
*/
public final class CollationElementIterator {
@@ -61,7 +66,7 @@
}
/**
- * Obtains the maximum length of any expansion sequence that ends with the
+ * Returns the maximum length of any expansion sequence that ends with the
* specified collation element. Returns {@code 1} if there is no expansion
* with this collation element as the last element.
*
@@ -69,15 +74,13 @@
* a collation element that has been previously obtained from a
* call to either the {@link #next()} or {@link #previous()}
* method.
- * @return the maximum length of any expansion sequence ending with the
- * specified collation element.
*/
public int getMaxExpansion(int order) {
return this.icuIterator.getMaxExpansion(order);
}
/**
- * Obtains the character offset in the source string corresponding to the
+ * Returns the character offset in the source string corresponding to the
* next collation element. This value could be any of:
* <ul>
* <li>The index of the first character in the source string that matches
@@ -94,42 +97,33 @@
* <li>The length of the source string, if iteration has reached the end.
* </li>
* </ul>
- *
- * @return The position of the collation element in the source string that
- * will be returned by the next invocation of the {@link #next()}
- * method.
*/
public int getOffset() {
return this.icuIterator.getOffset();
}
/**
- * Obtains the next collation element in the source string.
- *
- * @return the next collation element or {@code NULLORDER} if the end
- * of the iteration has been reached.
+ * Returns the next collation element in the source string or {@code NULLORDER} if
+ * the end of the iteration has been reached.
*/
public int next() {
return this.icuIterator.next();
}
/**
- * Obtains the previous collation element in the source string.
- *
- * @return the previous collation element, or {@code NULLORDER} when
- * the start of the iteration has been reached.
+ * Returns the previous collation element in the source string or {@code NULLORDER} if
+ * the start of the iteration has been reached.
*/
public int previous() {
return this.icuIterator.previous();
}
/**
- * Obtains the primary order of the specified collation element, i.e. the
+ * Returns the primary order of the specified collation element, i.e. the
* first 16 bits. This value is unsigned.
*
* @param order
* the element of the collation.
- * @return the element's 16 bit primary order.
*/
public static final int primaryOrder(int order) {
return CollationElementIteratorICU.primaryOrder(order);
@@ -149,12 +143,11 @@
}
/**
- * Obtains the secondary order of the specified collation element, i.e. the
+ * Returns the secondary order of the specified collation element, i.e. the
* 16th to 23th bits, inclusive. This value is unsigned.
*
* @param order
* the element of the collator.
- * @return the 8 bit secondary order of the element.
*/
public static final short secondaryOrder(int order) {
return (short) CollationElementIteratorICU.secondaryOrder(order);
@@ -209,12 +202,11 @@
}
/**
- * Obtains the tertiary order of the specified collation element, i.e. the
+ * Returns the tertiary order of the specified collation element, i.e. the
* last 8 bits. This value is unsigned.
*
* @param order
* the element of the collation.
- * @return the 8 bit tertiary order of the element.
*/
public static final short tertiaryOrder(int order) {
return (short) CollationElementIteratorICU.tertiaryOrder(order);
diff --git a/luni/src/main/java/java/text/DateFormat.java b/luni/src/main/java/java/text/DateFormat.java
index 00a282e..049ddf98 100644
--- a/luni/src/main/java/java/text/DateFormat.java
+++ b/luni/src/main/java/java/text/DateFormat.java
@@ -457,6 +457,12 @@
}
/**
+ * @hide for internal use only.
+ */
+ public static final void set24HourTimePref(boolean is24Hour) {
+ }
+
+ /**
* Returns the {@code NumberFormat} used by this {@code DateFormat}.
*
* @return the {@code NumberFormat} used by this date format.
diff --git a/luni/src/main/java/java/text/DecimalFormat.java b/luni/src/main/java/java/text/DecimalFormat.java
index f9f282a..fbbc172 100644
--- a/luni/src/main/java/java/text/DecimalFormat.java
+++ b/luni/src/main/java/java/text/DecimalFormat.java
@@ -487,6 +487,12 @@
* <em>escapes</em> the following character. If there is no character after
* the pad escape, then the pattern is illegal.</li>
* </ul>
+ *
+ * <h4>Serialization</h4>
+ * <p>
+ * Features marked as <strong><font color="red">NEW</font></strong> and patterns that use
+ * characters not documented above are unlikely to serialize/deserialize correctly.
+ *
* <h4>Synchronization</h4>
* <p>
* {@code DecimalFormat} objects are not synchronized. Multiple threads should
@@ -587,6 +593,9 @@
* if the pattern cannot be parsed.
*/
public void applyPattern(String pattern) {
+ // The underlying ICU4C accepts a super-set of the pattern spec documented by the Android
+ // APIs. For example, rounding increments (pattern characters '1'-'9'). They will work but
+ // see class doc for issues with serialization/deserialization they may cause.
ndf.applyPattern(pattern);
updateFieldsFromNative();
}
@@ -653,9 +662,6 @@
if (object == null) {
throw new NullPointerException("object == null");
}
- if (roundingMode == RoundingMode.UNNECESSARY && (object instanceof Float || object instanceof Double)) {
- checkRoundingUnnecessary(((Number) object).doubleValue());
- }
return ndf.formatToCharacterIterator(object);
}
@@ -668,31 +674,9 @@
}
}
- private void checkRoundingUnnecessary(Object value) {
- // ICU4C doesn't support this rounding mode, so we have to fake it.
- // This implementation reduces code duplication, but adds boxing
- // overhead and sends you all the way back through. But since we
- // have to format your string multiple times in this mode, you're already
- // screwed performance-wise.
- try {
- setRoundingMode(RoundingMode.UP);
- String upResult = format(value, new StringBuffer(), new FieldPosition(0)).toString();
- setRoundingMode(RoundingMode.DOWN);
- String downResult = format(value, new StringBuffer(), new FieldPosition(0)).toString();
- if (!upResult.equals(downResult)) {
- throw new ArithmeticException("rounding mode UNNECESSARY but rounding required");
- }
- } finally {
- setRoundingMode(RoundingMode.UNNECESSARY);
- }
- }
-
@Override
public StringBuffer format(double value, StringBuffer buffer, FieldPosition position) {
checkBufferAndFieldPosition(buffer, position);
- if (roundingMode == RoundingMode.UNNECESSARY) {
- checkRoundingUnnecessary(value);
- }
buffer.append(ndf.formatDouble(value, position));
return buffer;
}
@@ -700,9 +684,6 @@
@Override
public StringBuffer format(long value, StringBuffer buffer, FieldPosition position) {
checkBufferAndFieldPosition(buffer, position);
- if (roundingMode == RoundingMode.UNNECESSARY) {
- checkRoundingUnnecessary(value);
- }
buffer.append(ndf.formatLong(value, position));
return buffer;
}
@@ -1216,10 +1197,11 @@
throw new NullPointerException("roundingMode == null");
}
this.roundingMode = roundingMode;
- if (roundingMode != RoundingMode.UNNECESSARY) { // ICU4C doesn't support UNNECESSARY.
- double roundingIncrement = 1.0 / Math.pow(10, Math.max(0, getMaximumFractionDigits()));
- ndf.setRoundingMode(roundingMode, roundingIncrement);
- }
+ // DecimalFormat does not allow specification of a rounding increment. If anything other
+ // than 0.0 is used here the resulting DecimalFormat cannot be deserialized because the
+ // serialization format does not include rounding increment information.
+ double roundingIncrement = 0.0;
+ ndf.setRoundingMode(roundingMode, roundingIncrement);
}
public String toString() { return ndf.toString(); }
diff --git a/luni/src/main/java/java/text/DecimalFormatSymbols.java b/luni/src/main/java/java/text/DecimalFormatSymbols.java
index 708b291..1611594 100644
--- a/luni/src/main/java/java/text/DecimalFormatSymbols.java
+++ b/luni/src/main/java/java/text/DecimalFormatSymbols.java
@@ -50,7 +50,7 @@
private char percent;
private char perMill;
private char monetarySeparator;
- private char minusSign;
+ private String minusSign;
private String infinity, NaN, currencySymbol, intlCurrencySymbol;
private transient Currency currency;
@@ -179,7 +179,7 @@
groupingSeparator == obj.groupingSeparator &&
infinity.equals(obj.infinity) &&
intlCurrencySymbol.equals(obj.intlCurrencySymbol) &&
- minusSign == obj.minusSign &&
+ minusSign.equals(obj.minusSign) &&
monetarySeparator == obj.monetarySeparator &&
NaN.equals(obj.NaN) &&
patternSeparator == obj.patternSeparator &&
@@ -288,6 +288,16 @@
* @return the minus sign as a character.
*/
public char getMinusSign() {
+ if (minusSign.length() == 1) {
+ return minusSign.charAt(0);
+ }
+
+ throw new UnsupportedOperationException(
+ "Minus sign spans multiple characters: " + minusSign);
+ }
+
+ /** @hide */
+ public String getMinusSignString() {
return minusSign;
}
@@ -366,7 +376,7 @@
result = 31*result + percent;
result = 31*result + perMill;
result = 31*result + monetarySeparator;
- result = 31*result + minusSign;
+ result = 31*result + minusSign.hashCode();
result = 31*result + exponentSeparator.hashCode();
result = 31*result + infinity.hashCode();
result = 31*result + NaN.hashCode();
@@ -487,7 +497,7 @@
* the minus sign character.
*/
public void setMinusSign(char value) {
- this.minusSign = value;
+ this.minusSign = String.valueOf(value);
}
/**
diff --git a/luni/src/main/java/java/text/RuleBasedCollator.java b/luni/src/main/java/java/text/RuleBasedCollator.java
index cda06db..4e84ef7 100644
--- a/luni/src/main/java/java/text/RuleBasedCollator.java
+++ b/luni/src/main/java/java/text/RuleBasedCollator.java
@@ -20,243 +20,60 @@
import libcore.icu.RuleBasedCollatorICU;
/**
- * A concrete implementation class for {@code Collation}.
- * <p>
- * {@code RuleBasedCollator} has the following restrictions for efficiency
- * (other subclasses may be used for more complex languages):
- * <ol>
- * <li> If a French secondary ordering is specified it applies to the whole
- * collator object.</li>
- * <li> All non-mentioned Unicode characters are at the end of the collation
- * order.</li>
- * <li> If a character is not located in the {@code RuleBasedCollator}, the
- * default Unicode Collation Algorithm (UCA) rule-based table is automatically
- * searched as a backup.</li>
- * </ol>
- * <p>
- * The collation table is composed of a list of collation rules, where each rule
- * is of three forms:
+ * A concrete subclass of {@link Collator}.
+ * It is based on the ICU RuleBasedCollator which implements the
+ * CLDR and Unicode collation algorithms.
+ *
+ * <p>Most of the time, you create a {@link Collator} instance for a {@link java.util.Locale}
+ * by calling the {@link Collator#getInstance} factory method.
+ * You can construct a {@code RuleBasedCollator} if you need a custom sort order.
+ *
+ * <p>The root collator's sort order is the CLDR root collation order
+ * which in turn is the Unicode default sort order with a few modifications.
+ * A {@code RuleBasedCollator} is built from a rule {@code String} which changes the
+ * sort order of some characters and strings relative to the default order.
+ *
+ * <p>A rule string usually contains one or more rule chains.
+ * A rule chain consists of a reset followed by one or more rules.
+ * The reset anchors the following rules in the default sort order.
+ * The rules change the order of the their characters and strings
+ * relative to the reset point.
+ *
+ * <p>A reset is an ampersand {@code &} followed by one or more characters for the reset position.
+ * A rule is a relation operator, which specifies the level of difference,
+ * also followed by one or more characters.
+ * A multi-character rule creates a "contraction".
+ * A multi-character reset position usually creates "expansions".
+ *
+ * <p>For example, the following rules
+ * make "ä" sort with a diacritic-like (secondary) difference from "ae"
+ * (like in German phonebook sorting),
+ * and make "å" and "aa" sort as a base letter (primary) after "z" (like in Danish).
+ * Uppercase forms sort with a case-like (tertiary) difference after their lowercase forms.
+ *
* <blockquote>
* <pre>
- * <modifier>
- * <relation> <text-argument>
- * <reset> <text-argument>
+ * &AE<<ä <<<Ä
+ * &z<å<<<Å<<<aa<<<Aa<<<AA
* </pre>
* </blockquote>
- * <p>
- * The rule elements are defined as follows:
- * <ul type="disc">
- * <li><strong>Modifier</strong>: There is a single modifier which is used to
- * specify that all accents (secondary differences) are backwards:
- * <ul type=square>
- * <li>'@' : Indicates that accents are sorted backwards, as in French.
- * </ul>
- * </li>
- * <li><strong>Relation</strong>: The relations are the following:
- * <ul type=square>
- * <li>'<' : Greater, as a letter difference (primary)
- * <li>';' : Greater, as an accent difference (secondary)
- * <li>',' : Greater, as a case difference (tertiary)
- * <li>'=' : Equal
- * </ul>
- * </li>
- * <li><strong>Text-Argument</strong>: A text-argument is any sequence of
- * characters, excluding special characters (that is, common whitespace
- * characters [0009-000D, 0020] and rule syntax characters [0021-002F,
- * 003A-0040, 005B-0060, 007B-007E]). If those characters are desired, you can
- * put them in single quotes (for example, use '&' for ampersand). Note that
- * unquoted white space characters are ignored; for example, {@code b c} is
- * treated as {@code bc}.</li>
- * <li><strong>Reset</strong>: There is a single reset which is used primarily
- * for contractions and expansions, but which can also be used to add a
- * modification at the end of a set of rules:
- * <ul type=square>
- * <li>'&' : Indicates that the next rule follows the position to where the reset
- * text-argument would be sorted.
- * </ul>
- * </li>
- * </ul>
- * <p>
- * This sounds more complicated than it is in practice. For example, the
- * following are equivalent ways of expressing the same thing:
- * <blockquote>
*
- * <pre>
- * a < b < c
- * a < b & b < c
- * a < c & a < b
- * </pre>
- *
- * </blockquote>
- * <p>
- * Notice that the order is important, as the subsequent item goes immediately
- * after the text-argument. The following are not equivalent:
- * <blockquote>
- *
- * <pre>
- * a < b & a < c
- * a < c & a < b
- * </pre>
- *
- * </blockquote>
- * <p>
- * Either the text-argument must already be present in the sequence, or some
- * initial substring of the text-argument must be present. For example
- * {@code "a < b & ae < e"} is valid since "a" is present in the sequence before
- * "ae" is reset. In this latter case, "ae" is not entered and treated as a
- * single character; instead, "e" is sorted as if it were expanded to two
- * characters: "a" followed by an "e". This difference appears in natural
- * languages: in traditional Spanish "ch" is treated as if it contracts to a
- * single character (expressed as {@code "c < ch < d"}), while in traditional
- * German a-umlaut is treated as if it expands to two characters (expressed as
- * {@code "a,A < b,B ... & ae;\u00e3 & AE;\u00c3"}, where \u00e3 and \u00c3
- * are the escape sequences for a-umlaut).
- * <h4>Ignorable Characters</h4>
- * <p>
- * For ignorable characters, the first rule must start with a relation (the
- * examples we have used above are really fragments; {@code "a < b"} really
- * should be {@code "< a < b"}). If, however, the first relation is not
- * {@code "<"}, then all text-arguments up to the first {@code "<"} are
- * ignorable. For example, {@code ", - < a < b"} makes {@code "-"} an ignorable
- * character.
- * <h4>Normalization and Accents</h4>
- * <p>
- * {@code RuleBasedCollator} automatically processes its rule table to include
- * both pre-composed and combining-character versions of accented characters.
- * Even if the provided rule string contains only base characters and separate
- * combining accent characters, the pre-composed accented characters matching
- * all canonical combinations of characters from the rule string will be entered
- * in the table.
- * <p>
- * This allows you to use a RuleBasedCollator to compare accented strings even
- * when the collator is set to NO_DECOMPOSITION. However, if the strings to be
- * collated contain combining sequences that may not be in canonical order, you
- * should set the collator to CANONICAL_DECOMPOSITION to enable sorting of
- * combining sequences. For more information, see <a
- * href="http://www.aw.com/devpress">The Unicode Standard, Version 3.0</a>.
- * <h4>Errors</h4>
- * <p>
- * The following rules are not valid:
- * <ul type="disc">
- * <li>A text-argument contains unquoted punctuation symbols, for example
- * {@code "a < b-c < d"}.</li>
- * <li>A relation or reset character is not followed by a text-argument, for
- * example {@code "a < , b"}.</li>
- * <li>A reset where the text-argument (or an initial substring of the
- * text-argument) is not already in the sequence or allocated in the default UCA
- * table, for example {@code "a < b & e < f"}.</li>
+ * <p>For details see
+ * <ul>
+ * <li>CLDR <a href="http://www.unicode.org/reports/tr35/tr35-collation.html#Rules">Collation Rule Syntax</a>
+ * <li>ICU User Guide <a href="http://userguide.icu-project.org/collation/customization">Collation Customization</a>
* </ul>
- * <p>
- * If you produce one of these errors, {@code RuleBasedCollator} throws a
+ *
+ * <p>Note: earlier versions of {@code RuleBasedCollator} up to and including Android 4.4 (KitKat)
+ * allowed the omission of the reset from the first rule chain.
+ * This was interpreted as an implied reset after the last non-Han script in the default order.
+ * However, this is not a useful reset position, except for large tailorings of
+ * Han characters themselves.
+ * Starting with the CLDR 24 collation specification and the ICU 53 implementation,
+ * the initial reset is required.
+ *
+ * <p>If the rule string does not follow the syntax, then {@code RuleBasedCollator} throws a
* {@code ParseException}.
- * <h4>Examples</h4>
- * <p>
- * Normally, to create a rule-based collator object, you will use
- * {@code Collator}'s factory method {@code getInstance}. However, to create a
- * rule-based collator object with specialized rules tailored to your needs, you
- * construct the {@code RuleBasedCollator} with the rules contained in a
- * {@code String} object. For example:
- * <blockquote>
- *
- * <pre>
- * String Simple = "< a < b < c < d";
- * RuleBasedCollator mySimple = new RuleBasedCollator(Simple);
- * </pre>
- *
- * </blockquote>
- * <p>
- * Or:
- * <blockquote>
- *
- * <pre>
- * String Norwegian = "< a,A< b,B< c,C< d,D< e,E< f,F< g,G< h,H< i,I"
- * + "< j,J< k,K< l,L< m,M< n,N< o,O< p,P< q,Q< r,R"
- * + "< s,S< t,T< u,U< v,V< w,W< x,X< y,Y< z,Z"
- * + "< \u00E5=a\u030A,\u00C5=A\u030A"
- * + ";aa,AA< \u00E6,\u00C6< \u00F8,\u00D8";
- * RuleBasedCollator myNorwegian = new RuleBasedCollator(Norwegian);
- * </pre>
- *
- * </blockquote>
- * <p>
- * Combining {@code Collator}s is as simple as concatenating strings. Here is
- * an example that combines two {@code Collator}s from two different locales:
- * <blockquote>
- *
- * <pre>
- * // Create an en_US Collator object
- * RuleBasedCollator en_USCollator = (RuleBasedCollator)Collator
- * .getInstance(new Locale("en", "US", ""));
- *
- * // Create a da_DK Collator object
- * RuleBasedCollator da_DKCollator = (RuleBasedCollator)Collator
- * .getInstance(new Locale("da", "DK", ""));
- *
- * // Combine the two collators
- * // First, get the collation rules from en_USCollator
- * String en_USRules = en_USCollator.getRules();
- *
- * // Second, get the collation rules from da_DKCollator
- * String da_DKRules = da_DKCollator.getRules();
- *
- * RuleBasedCollator newCollator = new RuleBasedCollator(en_USRules + da_DKRules);
- * // newCollator has the combined rules
- * </pre>
- *
- * </blockquote>
- * <p>
- * The next example shows to make changes on an existing table to create a new
- * {@code Collator} object. For example, add {@code "& C < ch, cH, Ch, CH"} to
- * the {@code en_USCollator} object to create your own:
- * <blockquote>
- *
- * <pre>
- * // Create a new Collator object with additional rules
- * String addRules = "& C < ch, cH, Ch, CH";
- *
- * RuleBasedCollator myCollator = new RuleBasedCollator(en_USCollator + addRules);
- * // myCollator contains the new rules
- * </pre>
- *
- * </blockquote>
- * <p>
- * The following example demonstrates how to change the order of non-spacing
- * accents:
- * <blockquote>
- *
- * <pre>
- * // old rule
- * String oldRules = "= \u00a8 ; \u00af ; \u00bf" + "< a , A ; ae, AE ; \u00e6 , \u00c6"
- * + "< b , B < c, C < e, E & C < d, D";
- *
- * // change the order of accent characters
- * String addOn = "& \u00bf ; \u00af ; \u00a8;";
- *
- * RuleBasedCollator myCollator = new RuleBasedCollator(oldRules + addOn);
- * </pre>
- *
- * </blockquote>
- * <p>
- * The last example shows how to put new primary ordering in before the default
- * setting. For example, in the Japanese {@code Collator}, you can either sort
- * English characters before or after Japanese characters:
- * <blockquote>
- *
- * <pre>
- * // get en_US Collator rules
- * RuleBasedCollator en_USCollator = (RuleBasedCollator)
- * Collator.getInstance(Locale.US);
- *
- * // add a few Japanese character to sort before English characters
- * // suppose the last character before the first base letter 'a' in
- * // the English collation rule is \u30A2
- * String jaString = "& \u30A2 , \u30FC < \u30C8";
- *
- * RuleBasedCollator myJapaneseCollator =
- * new RuleBasedCollator(en_USCollator.getRules() + jaString);
- * </pre>
- *
- * </blockquote>
*/
public class RuleBasedCollator extends Collator {
RuleBasedCollator(RuleBasedCollatorICU wrapper) {
@@ -265,13 +82,11 @@
/**
* Constructs a new instance of {@code RuleBasedCollator} using the
- * specified {@code rules}. The {@code rules} are usually either
- * hand-written based on the {@link RuleBasedCollator class description} or
- * the result of a former {@link #getRules()} call.
+ * specified {@code rules}. (See the {@link RuleBasedCollator class description}.)
* <p>
- * Note that the {@code rules} are actually interpreted as a delta to the
- * standard Unicode Collation Algorithm (UCA). This differs
- * slightly from other implementations which work with full {@code rules}
+ * Note that the {@code rules} are interpreted as a delta to the
+ * default sort order. This differs
+ * from other implementations which work with full {@code rules}
* specifications and may result in different behavior.
*
* @param rules
@@ -286,9 +101,6 @@
if (rules == null) {
throw new NullPointerException("rules == null");
}
- if (rules.isEmpty()) {
- throw new ParseException("empty rules", 0);
- }
try {
icuColl = new RuleBasedCollatorICU(rules);
} catch (Exception e) {
@@ -336,14 +148,9 @@
/**
* Returns the collation rules of this collator. These {@code rules} can be
* fed into the {@code RuleBasedCollator(String)} constructor.
- * <p>
- * Note that the {@code rules} are actually interpreted as a delta to the
- * standard Unicode Collation Algorithm (UCA). Hence, an empty {@code rules}
- * string results in the default UCA rules being applied. This differs
- * slightly from other implementations which work with full {@code rules}
- * specifications and may result in different behavior.
*
- * @return the collation rules.
+ * <p>The returned string will be empty unless you constructed the instance yourself.
+ * The string forms of the collation rules are omitted to save space on the device.
*/
public String getRules() {
return icuColl.getRules();
@@ -367,13 +174,6 @@
* the collation rules, strength and decomposition mode for this
* {@code RuleBasedCollator}. See the {@code Collator} class description
* for an example of use.
- * <p>
- * General recommendation: If comparisons are to be done with the same strings
- * multiple times, it is more efficient to generate {@code CollationKey}
- * objects for the strings and use
- * {@code CollationKey.compareTo(CollationKey)} for the comparisons. If each
- * string is compared to only once, using
- * {@code RuleBasedCollator.compare(String, String)} has better performance.
*
* @param source
* the source text.
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index cd3663d..f832a6e 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -1103,25 +1103,7 @@
}
if (max == 0) {
position.setIndex(index);
- Number n = numberFormat.parse(string, position);
- // In RTL locales, NumberFormat might have parsed "2012-" in an ISO date as the
- // negative number -2012.
- // Ideally, we wouldn't have this broken API that exposes a NumberFormat and expects
- // us to use it. The next best thing would be a way to ask the NumberFormat to parse
- // positive numbers only, but icu4c supports negative (BCE) years. The best we can do
- // is try to recognize when icu4c has done this, and undo it.
- if (n != null && n.longValue() < 0) {
- if (numberFormat instanceof DecimalFormat) {
- DecimalFormat df = (DecimalFormat) numberFormat;
- char lastChar = string.charAt(position.getIndex() - 1);
- char minusSign = df.getDecimalFormatSymbols().getMinusSign();
- if (lastChar == minusSign) {
- n = Long.valueOf(-n.longValue()); // Make the value positive.
- position.setIndex(position.getIndex() - 1); // Spit out the negative sign.
- }
- }
- }
- return n;
+ return numberFormat.parse(string, position);
}
int result = 0;
diff --git a/luni/src/main/java/java/util/Currency.java b/luni/src/main/java/java/util/Currency.java
index b5b04a0..e8ecdde 100644
--- a/luni/src/main/java/java/util/Currency.java
+++ b/luni/src/main/java/java/util/Currency.java
@@ -127,6 +127,17 @@
}
/**
+ * Returns the ISO 4217 numeric code for this currency. If there is no standard numeric code a
+ * zero is returned.
+ *
+ * @since 1.7
+ * @hide Until ready for an API update
+ */
+ public int getNumericCode() {
+ return ICU.getCurrencyNumericCode(currencyCode);
+ }
+
+ /**
* Equivalent to {@code getSymbol(Locale.getDefault())}.
* See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
*/
diff --git a/luni/src/main/java/java/util/Formatter.java b/luni/src/main/java/java/util/Formatter.java
index 98bdccc..dd9a09a 100644
--- a/luni/src/main/java/java/util/Formatter.java
+++ b/luni/src/main/java/java/util/Formatter.java
@@ -2065,7 +2065,7 @@
formatToken.setPrecision(FormatToken.UNSET);
int startIndex = 0;
- if (result.charAt(0) == localeData.minusSign) {
+ if (startsWithMinusSign(result, localeData.minusSign)) {
if (formatToken.flagParenthesis) {
return wrapParentheses(result);
}
@@ -2081,8 +2081,9 @@
}
char firstChar = result.charAt(0);
- if (formatToken.flagZero && (firstChar == '+' || firstChar == localeData.minusSign)) {
- startIndex = 1;
+ if (formatToken.flagZero && (firstChar == '+' ||
+ startsWithMinusSign(result, localeData.minusSign))) {
+ startIndex = localeData.minusSign.length();
}
if (conversionType == 'a' || conversionType == 'A') {
@@ -2091,6 +2092,20 @@
return padding(result, startIndex);
}
+ private static boolean startsWithMinusSign(CharSequence cs, String minusSign) {
+ if (cs.length() < minusSign.length()) {
+ return false;
+ }
+
+ for (int i = 0; i < minusSign.length(); ++i) {
+ if (minusSign.charAt(i) != cs.charAt(i)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private void transformE(StringBuilder result) {
// All zeros in this method are *pattern* characters, so no localization.
final int precision = formatToken.getPrecision();
diff --git a/luni/src/main/java/java/util/GregorianCalendar.java b/luni/src/main/java/java/util/GregorianCalendar.java
index df6d772..34cf97e 100644
--- a/luni/src/main/java/java/util/GregorianCalendar.java
+++ b/luni/src/main/java/java/util/GregorianCalendar.java
@@ -458,9 +458,9 @@
complete();
}
- private void fullFieldsCalc(long timeVal, int zoneOffset) {
+ private void fullFieldsCalc(int zoneOffset) {
int millis = (int) (time % 86400000);
- long days = timeVal / 86400000;
+ long days = time / 86400000;
if (millis < 0) {
millis += 86400000;
@@ -477,9 +477,9 @@
days++;
}
- int dayOfYear = computeYearAndDay(days, timeVal + zoneOffset);
+ int dayOfYear = computeYearAndDay(days, time + zoneOffset);
fields[DAY_OF_YEAR] = dayOfYear;
- if(fields[YEAR] == changeYear && gregorianCutover <= timeVal + zoneOffset){
+ if(fields[YEAR] == changeYear && gregorianCutover <= time + zoneOffset){
dayOfYear += currentYearSkew;
}
int month = dayOfYear / 32;
@@ -507,10 +507,10 @@
days++;
}
if (oldDays != days) {
- dayOfYear = computeYearAndDay(days, timeVal - zoneOffset
+ dayOfYear = computeYearAndDay(days, time - zoneOffset
+ dstOffset);
fields[DAY_OF_YEAR] = dayOfYear;
- if(fields[YEAR] == changeYear && gregorianCutover <= timeVal - zoneOffset + dstOffset){
+ if(fields[YEAR] == changeYear && gregorianCutover <= time - zoneOffset + dstOffset){
dayOfYear += currentYearSkew;
}
month = dayOfYear / 32;
@@ -570,7 +570,7 @@
fields[DST_OFFSET] = dstOffset;
fields[ZONE_OFFSET] = zoneOffset;
- fullFieldsCalc(time, zoneOffset);
+ fullFieldsCalc(zoneOffset);
for (int i = 0; i < FIELD_COUNT; i++) {
isSet[i] = true;
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index 885d150..163a627 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -80,21 +80,25 @@
* <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-8">CLDR 1.8</a></td>
* <td><a href="http://www.unicode.org/versions/Unicode5.2.0/">Unicode 5.2</a></td></tr>
* <tr><td>Android 4.0 (Ice Cream Sandwich)</td>
- * <td>ICU 4.6</td>
+ * <td><a href="http://site.icu-project.org/download/46">ICU 4.6</a></td>
* <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-9">CLDR 1.9</a></td>
* <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
* <tr><td>Android 4.1 (Jelly Bean)</td>
- * <td>ICU 4.8</td>
+ * <td><a href="http://site.icu-project.org/download/48">ICU 4.8</a></td>
* <td><a href="http://cldr.unicode.org/index/downloads/cldr-2-0">CLDR 2.0</a></td>
* <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
* <tr><td>Android 4.3 (Jelly Bean MR2)</td>
- * <td>ICU 50</td>
+ * <td><a href="http://site.icu-project.org/download/50">ICU 50</a></td>
* <td><a href="http://cldr.unicode.org/index/downloads/cldr-22-1">CLDR 22.1</a></td>
* <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
* <tr><td>Android 4.4 (KitKat)</td>
- * <td>ICU 51</td>
+ * <td><a href="http://site.icu-project.org/download/51">ICU 51</a></td>
* <td><a href="http://cldr.unicode.org/index/downloads/cldr-23">CLDR 23</a></td>
* <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
+ * <tr><td>Android 4.? (STOPSHIP)</td>
+ * <td><a href="http://site.icu-project.org/download/53">ICU 53</a></td>
+ * <td><a href="http://cldr.unicode.org/index/downloads/cldr-25">CLDR 25</a></td>
+ * <td><a href="http://www.unicode.org/versions/Unicode6.3.0/">Unicode 6.3</a></td></tr>
* </table>
*
* <a name="default_locale"><h3>Be wary of the default locale</h3></a>
@@ -1034,13 +1038,14 @@
/**
* Returns this locale's language name, country name, and variant, localized
* to {@code locale}. The exact output form depends on whether this locale
- * corresponds to a specific language, country and variant.
+ * corresponds to a specific language, script, country and variant.
*
* <p>For example:
* <ul>
* <li>{@code new Locale("en").getDisplayName(Locale.US)} -> {@code English}
* <li>{@code new Locale("en", "US").getDisplayName(Locale.US)} -> {@code English (United States)}
* <li>{@code new Locale("en", "US", "POSIX").getDisplayName(Locale.US)} -> {@code English (United States,Computer)}
+ * <li>{@code Locale.fromLanguageTag("zh-Hant-CN").getDisplayName(Locale.US)} -> {@code Chinese (Traditional Han,China)}
* <li>{@code new Locale("en").getDisplayName(Locale.FRANCE)} -> {@code anglais}
* <li>{@code new Locale("en", "US").getDisplayName(Locale.FRANCE)} -> {@code anglais (États-Unis)}
* <li>{@code new Locale("en", "US", "POSIX").getDisplayName(Locale.FRANCE)} -> {@code anglais (États-Unis,informatique)}.
@@ -1059,12 +1064,14 @@
buffer.append(" (");
}
String displayScript = getDisplayScript(locale);
- buffer.append(displayScript.isEmpty() ? countryCode : displayScript);
+ buffer.append(displayScript.isEmpty() ? scriptCode : displayScript);
++count;
}
if (!countryCode.isEmpty()) {
if (count == 1) {
buffer.append(" (");
+ } else if (count == 2) {
+ buffer.append(",");
}
String displayCountry = getDisplayCountry(locale);
buffer.append(displayCountry.isEmpty() ? countryCode : displayCountry);
@@ -1073,7 +1080,7 @@
if (!variantCode.isEmpty()) {
if (count == 1) {
buffer.append(" (");
- } else if (count == 2) {
+ } else if (count == 2 || count == 3) {
buffer.append(",");
}
String displayVariant = getDisplayVariant(locale);
@@ -1334,6 +1341,7 @@
throw new NullPointerException("locale == null");
}
defaultLocale = locale;
+ ICU.setDefaultLocale(ICU.localeIdFromLocale(locale));
}
/**
diff --git a/luni/src/main/java/java/util/UUID.java b/luni/src/main/java/java/util/UUID.java
index 3594d87..020ac95 100644
--- a/luni/src/main/java/java/util/UUID.java
+++ b/luni/src/main/java/java/util/UUID.java
@@ -182,28 +182,17 @@
throw new NullPointerException("uuid == null");
}
- int[] position = new int[5];
- int lastPosition = 1;
- int startPosition = 0;
-
- int i = 0;
- for (; i < position.length && lastPosition > 0; i++) {
- position[i] = uuid.indexOf("-", startPosition);
- lastPosition = position[i];
- startPosition = position[i] + 1;
- }
-
- // should have and only can have four "-" in UUID
- if (i != position.length || lastPosition != -1) {
+ String[] parts = uuid.split("-");
+ if (parts.length != 5) {
throw new IllegalArgumentException("Invalid UUID: " + uuid);
}
- long m1 = Long.parseLong(uuid.substring(0, position[0]), 16);
- long m2 = Long.parseLong(uuid.substring(position[0] + 1, position[1]), 16);
- long m3 = Long.parseLong(uuid.substring(position[1] + 1, position[2]), 16);
+ long m1 = Long.parsePositiveLong(parts[0], 16);
+ long m2 = Long.parsePositiveLong(parts[1], 16);
+ long m3 = Long.parsePositiveLong(parts[2], 16);
- long lsb1 = Long.parseLong(uuid.substring(position[2] + 1, position[3]), 16);
- long lsb2 = Long.parseLong(uuid.substring(position[3] + 1), 16);
+ long lsb1 = Long.parsePositiveLong(parts[3], 16);
+ long lsb2 = Long.parsePositiveLong(parts[4], 16);
long msb = (m1 << 32) | (m2 << 16) | m3;
long lsb = (lsb1 << 48) | lsb2;
diff --git a/luni/src/main/java/java/util/logging/SocketHandler.java b/luni/src/main/java/java/util/logging/SocketHandler.java
index 85a9e6c..48bfc0e 100644
--- a/luni/src/main/java/java/util/logging/SocketHandler.java
+++ b/luni/src/main/java/java/util/logging/SocketHandler.java
@@ -108,12 +108,9 @@
// check the validity of the port number
int p = 0;
try {
- p = Integer.parseInt(port);
+ p = Integer.parsePositiveInt(port);
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Illegal port argument");
- }
- if (p <= 0) {
- throw new IllegalArgumentException("Illegal port argument");
+ throw new IllegalArgumentException("Illegal port argument " + port);
}
// establish the network connection
try {
diff --git a/luni/src/main/java/java/util/zip/GZIPInputStream.java b/luni/src/main/java/java/util/zip/GZIPInputStream.java
index 790adb2..1bfc496 100644
--- a/luni/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/luni/src/main/java/java/util/zip/GZIPInputStream.java
@@ -166,10 +166,14 @@
// successfully consumed the GZIP trailer.
final int remaining = inf.getRemaining() - GZIP_TRAILER_SIZE;
if (remaining > 0) {
- // NOTE: This prevents us from creating multiple layers of nested
- // PushbackInputStreams if we have multiple members in this stream.
+ // NOTE: We make sure we create a pushback stream exactly once,
+ // even if the input stream contains multiple members.
+ //
+ // The push back stream we create must therefore be able to contain
+ // (worst case) the entire buffer even though there may be fewer bytes
+ // remaining when it is first created.
if (!(in instanceof PushbackInputStream)) {
- in = new PushbackInputStream(in, BUF_SIZE);
+ in = new PushbackInputStream(in, buf.length);
}
((PushbackInputStream) in).unread(buf,
inf.getCurrentOffset() + GZIP_TRAILER_SIZE, remaining);
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java
index 69f027a..217cc3c 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -20,6 +20,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Calendar;
@@ -94,9 +95,7 @@
if (name == null) {
throw new NullPointerException("name == null");
}
- if (name.length() > 0xFFFF) {
- throw new IllegalArgumentException("Name too long: " + name.length());
- }
+ validateStringLength("Name", name);
this.name = name;
}
@@ -203,11 +202,8 @@
this.comment = null;
return;
}
+ validateStringLength("Comment", comment);
- byte[] commentBytes = comment.getBytes(StandardCharsets.UTF_8);
- if (commentBytes.length > 0xffff) {
- throw new IllegalArgumentException("Comment too long: " + commentBytes.length);
- }
this.comment = comment;
}
@@ -375,12 +371,14 @@
/*
* Internal constructor. Creates a new ZipEntry by reading the
* Central Directory Entry (CDE) from "in", which must be positioned
- * at the CDE signature.
+ * at the CDE signature. If the GPBF_UTF8_FLAG is set in the CDE then
+ * UTF-8 is used to decode the string information, otherwise the
+ * defaultCharset is used.
*
* On exit, "in" will be positioned at the start of the next entry
* in the Central Directory.
*/
- ZipEntry(byte[] cdeHdrBuf, InputStream cdStream) throws IOException {
+ ZipEntry(byte[] cdeHdrBuf, InputStream cdStream, Charset defaultCharset) throws IOException {
Streams.readFully(cdStream, cdeHdrBuf, 0, cdeHdrBuf.length);
BufferIterator it = HeapBufferIterator.iterator(cdeHdrBuf, 0, cdeHdrBuf.length,
@@ -398,6 +396,13 @@
throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf);
}
+ // If the GPBF_UTF8_FLAG is set then the character encoding is UTF-8 whatever the default
+ // provided.
+ Charset charset = defaultCharset;
+ if ((gpbf & ZipFile.GPBF_UTF8_FLAG) != 0) {
+ charset = StandardCharsets.UTF_8;
+ }
+
compressionMethod = it.readShort() & 0xffff;
time = it.readShort() & 0xffff;
modDate = it.readShort() & 0xffff;
@@ -420,19 +425,17 @@
if (containsNulByte(nameBytes)) {
throw new ZipException("Filename contains NUL byte: " + Arrays.toString(nameBytes));
}
- name = new String(nameBytes, 0, nameBytes.length, StandardCharsets.UTF_8);
+ name = new String(nameBytes, 0, nameBytes.length, charset);
if (extraLength > 0) {
extra = new byte[extraLength];
Streams.readFully(cdStream, extra, 0, extraLength);
}
- // The RI has always assumed UTF-8. (If GPBF_UTF8_FLAG isn't set, the encoding is
- // actually IBM-437.)
if (commentByteCount > 0) {
byte[] commentBytes = new byte[commentByteCount];
Streams.readFully(cdStream, commentBytes, 0, commentByteCount);
- comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);
+ comment = new String(commentBytes, 0, commentBytes.length, charset);
}
}
@@ -444,4 +447,14 @@
}
return false;
}
+
+ private static void validateStringLength(String argument, String string) {
+ // This check is not perfect: the character encoding is determined when the entry is
+ // written out. UTF-8 is probably a worst-case: most alternatives should be single byte per
+ // character.
+ byte[] bytes = string.getBytes(StandardCharsets.UTF_8);
+ if (bytes.length > 0xffff) {
+ throw new IllegalArgumentException(argument + " too long: " + bytes.length);
+ }
+ }
}
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java
index 40036cf..4b3e431 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -26,6 +26,7 @@
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Iterator;
@@ -96,6 +97,8 @@
private final String filename;
+ private final Charset charset;
+
private File fileToDeleteOnClose;
private RandomAccessFile raf;
@@ -108,33 +111,81 @@
/**
* Constructs a new {@code ZipFile} allowing read access to the contents of the given file.
+ *
+ * <p>UTF-8 is used to decode all comments and entry names in the file.
+ *
* @throws ZipException if a zip error occurs.
* @throws IOException if an {@code IOException} occurs.
*/
public ZipFile(File file) throws ZipException, IOException {
- this(file, OPEN_READ);
+ this(file, OPEN_READ, StandardCharsets.UTF_8);
}
/**
* Constructs a new {@code ZipFile} allowing read access to the contents of the given file.
+ *
+ * <p>The {@code charset} is used to decode the file comment if one exists. If the character
+ * encoding for entry names and comments is not explicitly marked as UTF-8 by the zip file
+ * then {@code charset} is used to decode them.
+ *
+ * @throws ZipException if a zip error occurs.
+ * @throws IOException if an {@code IOException} occurs.
+ * @since 1.7
+ * @hide Until ready for an API update
+ */
+ public ZipFile(File file, Charset charset) throws ZipException, IOException {
+ this(file, OPEN_READ, charset);
+ }
+
+ /**
+ * Constructs a new {@code ZipFile} allowing read access to the contents of the given file.
+ *
+ * <p>UTF-8 is used to decode all comments and entry names in the file.
+ *
* @throws IOException if an IOException occurs.
*/
public ZipFile(String name) throws IOException {
- this(new File(name), OPEN_READ);
+ this(new File(name), OPEN_READ, StandardCharsets.UTF_8);
}
/**
* Constructs a new {@code ZipFile} allowing access to the given file.
- * The {@code mode} must be either {@code OPEN_READ} or {@code OPEN_READ|OPEN_DELETE}.
*
- * <p>If the {@code OPEN_DELETE} flag is supplied, the file will be deleted at or before the
+ * <p>UTF-8 is used to decode all comments and entry names in the file.
+ *
+ * <p>The {@code mode} must be either {@code OPEN_READ} or {@code OPEN_READ|OPEN_DELETE}.
+ * If the {@code OPEN_DELETE} flag is supplied, the file will be deleted at or before the
* time that the {@code ZipFile} is closed (the contents will remain accessible until
* this {@code ZipFile} is closed); it also calls {@code File.deleteOnExit}.
*
* @throws IOException if an {@code IOException} occurs.
*/
public ZipFile(File file, int mode) throws IOException {
+ this(file, mode, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Constructs a new {@code ZipFile} allowing access to the given file.
+ *
+ * <p>The {@code mode} must be either {@code OPEN_READ} or {@code OPEN_READ|OPEN_DELETE}.
+ * If the {@code OPEN_DELETE} flag is supplied, the file will be deleted at or before the
+ * time that the {@code ZipFile} is closed (the contents will remain accessible until
+ * this {@code ZipFile} is closed); it also calls {@code File.deleteOnExit}.
+ *
+ * <p>The {@code charset} is used to decode the file comment if one exists. If the character
+ * encoding for entry names and comments is not explicitly marked as UTF-8 by the zip file
+ * then {@code charset} is used to decode them.
+ *
+ * @throws IOException if an {@code IOException} occurs.
+ * @since 1.7
+ * @hide Until ready for an API update
+ */
+ public ZipFile(File file, int mode, Charset charset) throws IOException {
filename = file.getPath();
+ if (charset == null) {
+ throw new NullPointerException("charset == null");
+ }
+ this.charset = charset;
if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE)) {
throw new IllegalArgumentException("Bad mode: " + mode);
}
@@ -357,6 +408,9 @@
raf.seek(0);
final int headerMagic = Integer.reverseBytes(raf.readInt());
+ if (headerMagic == ENDSIG) {
+ throw new ZipException("Empty zip archive not supported");
+ }
if (headerMagic != LOCSIG) {
throw new ZipException("Not a zip archive");
}
@@ -400,7 +454,7 @@
if (commentLength > 0) {
byte[] commentBytes = new byte[commentLength];
raf.readFully(commentBytes);
- comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);
+ comment = new String(commentBytes, 0, commentBytes.length, charset);
}
// Seek to the first CDE and read all entries.
@@ -411,7 +465,7 @@
BufferedInputStream bufferedStream = new BufferedInputStream(rafStream, 4096);
byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry.
for (int i = 0; i < numEntries; ++i) {
- ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream);
+ ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream, charset);
if (newEntry.localHeaderRelOffset >= centralDirOffset) {
throw new ZipException("Local file header offset is after central directory");
}
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index 17f3938..5a73619 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -21,7 +21,9 @@
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.nio.ByteOrder;
+import java.nio.charset.Charset;
import java.nio.charset.ModifiedUtf8;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import libcore.io.Memory;
import libcore.io.Streams;
@@ -84,18 +86,39 @@
private final CRC32 crc = new CRC32();
- private byte[] nameBuf = new byte[256];
+ private byte[] stringBytesBuf = new byte[256];
- private char[] charBuf = new char[256];
+ private char[] stringCharBuf = new char[256];
+
+ private final Charset charset;
/**
* Constructs a new {@code ZipInputStream} to read zip entries from the given input stream.
+ *
+ * <p>UTF-8 is used to decode all strings in the file.
*/
public ZipInputStream(InputStream stream) {
+ this(stream, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Constructs a new {@code ZipInputStream} to read zip entries from the given input stream.
+ *
+ * <p>If the character encoding for entry names and comments is not explicitly marked as UTF-8
+ * by the zip file then {@code charset} is used to decode them.
+ *
+ * @since 1.7
+ * @hide Until ready for an API update
+ */
+ public ZipInputStream(InputStream stream, Charset charset) {
super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true));
if (stream == null) {
throw new NullPointerException("stream == null");
}
+ if (charset == null) {
+ throw new NullPointerException("charset == null");
+ }
+ this.charset = charset;
}
/**
@@ -249,14 +272,13 @@
}
int extraLength = peekShort(LOCEXT - LOCVER);
- if (nameLength > nameBuf.length) {
- nameBuf = new byte[nameLength];
- // The bytes are modified UTF-8, so the number of chars will always be less than or
- // equal to the number of bytes. It's fine if this buffer is too long.
- charBuf = new char[nameLength];
+ // Determine the character set to use to decode strings.
+ Charset charset = this.charset;
+ if ((flags & ZipFile.GPBF_UTF8_FLAG) != 0) {
+ charset = StandardCharsets.UTF_8;
}
- Streams.readFully(in, nameBuf, 0, nameLength);
- currentEntry = createZipEntry(ModifiedUtf8.decode(nameBuf, charBuf, 0, nameLength));
+ String name = readString(nameLength, charset);
+ currentEntry = createZipEntry(name);
currentEntry.time = ceLastModifiedTime;
currentEntry.modDate = ceLastModifiedDate;
currentEntry.setMethod(ceCompressionMethod);
@@ -273,6 +295,26 @@
return currentEntry;
}
+ /**
+ * Reads bytes from the current stream position returning the string representation.
+ */
+ private String readString(int byteLength, Charset charset) throws IOException {
+ if (byteLength > stringBytesBuf.length) {
+ stringBytesBuf = new byte[byteLength];
+ }
+ Streams.readFully(in, stringBytesBuf, 0, byteLength);
+ if (charset == StandardCharsets.UTF_8) {
+ // The number of chars will always be less than or equal to the number of bytes. It's
+ // fine if this buffer is too long.
+ if (byteLength > stringCharBuf.length) {
+ stringCharBuf = new char[byteLength];
+ }
+ return ModifiedUtf8.decode(stringBytesBuf, stringCharBuf, 0, byteLength);
+ } else {
+ return new String(stringBytesBuf, 0, byteLength, charset);
+ }
+ }
+
private int peekShort(int offset) {
return Memory.peekShort(hdrBuf, offset, ByteOrder.LITTLE_ENDIAN) & 0xffff;
}
diff --git a/luni/src/main/java/java/util/zip/ZipOutputStream.java b/luni/src/main/java/java/util/zip/ZipOutputStream.java
index c4d7560..ac6bdf2 100644
--- a/luni/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipOutputStream.java
@@ -20,6 +20,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
@@ -75,6 +76,8 @@
private final HashSet<String> entries = new HashSet<String>();
+ private final Charset charset;
+
private int defaultCompressionMethod = DEFLATED;
private int compressionLevel = Deflater.DEFAULT_COMPRESSION;
@@ -85,16 +88,40 @@
private final CRC32 crc = new CRC32();
- private int offset = 0, curOffset = 0, nameLength;
+ private int offset = 0, curOffset = 0;
+ /** The charset-encoded name for the current entry. */
private byte[] nameBytes;
+ /** The charset-encoded comment for the current entry. */
+ private byte[] entryCommentBytes;
+
/**
- * Constructs a new {@code ZipOutputStream} that writes a zip file
- * to the given {@code OutputStream}.
+ * Constructs a new {@code ZipOutputStream} that writes a zip file to the given
+ * {@code OutputStream}.
+ *
+ * <p>UTF-8 will be used to encode the file comment, entry names and comments.
*/
public ZipOutputStream(OutputStream os) {
+ this(os, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Constructs a new {@code ZipOutputStream} that writes a zip file to the given
+ * {@code OutputStream}.
+ *
+ * <p>The specified character set will be used to encode the file comment, entry names and
+ * comments.
+ *
+ * @since 1.7
+ * @hide Until ready for an API update
+ */
+ public ZipOutputStream(OutputStream os, Charset charset) {
super(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+ if (charset == null) {
+ throw new NullPointerException("charset == null");
+ }
+ this.charset = charset;
}
/**
@@ -153,10 +180,12 @@
// Update the CentralDirectory
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
int flags = currentEntry.getMethod() == STORED ? 0 : ZipFile.GPBF_DATA_DESCRIPTOR_FLAG;
- // Since gingerbread, we always set the UTF-8 flag on individual files.
- // Some tools insist that the central directory also have the UTF-8 flag.
+ // Since gingerbread, we always set the UTF-8 flag on individual files if appropriate.
+ // Some tools insist that the central directory have the UTF-8 flag.
// http://code.google.com/p/android/issues/detail?id=20214
- flags |= ZipFile.GPBF_UTF8_FLAG;
+ if (charset == StandardCharsets.UTF_8) {
+ flags |= ZipFile.GPBF_UTF8_FLAG;
+ }
writeLong(cDir, CENSIG);
writeShort(cDir, ZIP_VERSION_2_0); // Version this file was made by.
writeShort(cDir, ZIP_VERSION_2_0); // Minimum version needed to extract.
@@ -172,19 +201,14 @@
curOffset += writeLong(cDir, crc.tbytes);
writeLong(cDir, crc.tbytes);
}
- curOffset += writeShort(cDir, nameLength);
+ curOffset += writeShort(cDir, nameBytes.length);
if (currentEntry.extra != null) {
curOffset += writeShort(cDir, currentEntry.extra.length);
} else {
writeShort(cDir, 0);
}
- String comment = currentEntry.getComment();
- byte[] commentBytes = EmptyArray.BYTE;
- if (comment != null) {
- commentBytes = comment.getBytes(StandardCharsets.UTF_8);
- }
- writeShort(cDir, commentBytes.length); // Comment length.
+ writeShort(cDir, entryCommentBytes.length); // Comment length.
writeShort(cDir, 0); // Disk Start
writeShort(cDir, 0); // Internal File Attributes
writeLong(cDir, 0); // External File Attributes
@@ -195,8 +219,9 @@
cDir.write(currentEntry.extra);
}
offset += curOffset;
- if (commentBytes.length > 0) {
- cDir.write(commentBytes);
+ if (entryCommentBytes.length > 0) {
+ cDir.write(entryCommentBytes);
+ entryCommentBytes = EmptyArray.BYTE;
}
currentEntry = null;
crc.reset();
@@ -294,10 +319,14 @@
// TODO: support Zip64.
throw new ZipException("Too many entries for the zip file format's 16-bit entry count");
}
- nameBytes = ze.name.getBytes(StandardCharsets.UTF_8);
- nameLength = nameBytes.length;
- if (nameLength > 0xffff) {
- throw new IllegalArgumentException("Name too long: " + nameLength + " UTF-8 bytes");
+ nameBytes = ze.name.getBytes(charset);
+ checkSizeIsWithinShort("Name", nameBytes);
+ entryCommentBytes = EmptyArray.BYTE;
+ if (ze.comment != null) {
+ entryCommentBytes = ze.comment.getBytes(charset);
+ // The comment is not written out until the entry is finished, but it is validated here
+ // to fail-fast.
+ checkSizeIsWithinShort("Comment", entryCommentBytes);
}
def.setLevel(compressionLevel);
@@ -310,8 +339,10 @@
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
int flags = (method == STORED) ? 0 : ZipFile.GPBF_DATA_DESCRIPTOR_FLAG;
// Java always outputs UTF-8 filenames. (Before Java 7, the RI didn't set this flag and used
- // modified UTF-8. From Java 7, it sets this flag and uses normal UTF-8.)
- flags |= ZipFile.GPBF_UTF8_FLAG;
+ // modified UTF-8. From Java 7, when using UTF_8 it sets this flag and uses normal UTF-8.)
+ if (charset == StandardCharsets.UTF_8) {
+ flags |= ZipFile.GPBF_UTF8_FLAG;
+ }
writeLong(out, LOCSIG); // Entry header
writeShort(out, ZIP_VERSION_2_0); // Minimum version needed to extract.
writeShort(out, flags);
@@ -331,7 +362,7 @@
writeLong(out, 0);
writeLong(out, 0);
}
- writeShort(out, nameLength);
+ writeShort(out, nameBytes.length);
if (currentEntry.extra != null) {
writeShort(out, currentEntry.extra.length);
} else {
@@ -345,18 +376,16 @@
/**
* Sets the comment associated with the file being written. See {@link ZipFile#getComment}.
- * @throws IllegalArgumentException if the comment is >= 64 Ki UTF-8 bytes.
+ * @throws IllegalArgumentException if the comment is >= 64 Ki encoded bytes.
*/
public void setComment(String comment) {
if (comment == null) {
- this.commentBytes = null;
+ this.commentBytes = EmptyArray.BYTE;
return;
}
- byte[] newCommentBytes = comment.getBytes(StandardCharsets.UTF_8);
- if (newCommentBytes.length > 0xffff) {
- throw new IllegalArgumentException("Comment too long: " + newCommentBytes.length + " bytes");
- }
+ byte[] newCommentBytes = comment.getBytes(charset);
+ checkSizeIsWithinShort("Comment", newCommentBytes);
this.commentBytes = newCommentBytes;
}
@@ -423,4 +452,11 @@
throw new IOException("Stream is closed");
}
}
+
+ private void checkSizeIsWithinShort(String property, byte[] bytes) {
+ if (bytes.length > 0xffff) {
+ throw new IllegalArgumentException(
+ property + " too long in " + charset + ":" + bytes.length + " bytes");
+ }
+ }
}
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java
index 6299f80..2e3b341 100644
--- a/luni/src/main/java/javax/crypto/Cipher.java
+++ b/luni/src/main/java/javax/crypto/Cipher.java
@@ -129,11 +129,21 @@
private Provider provider;
/**
+ * The provider specified when instance created.
+ */
+ private final Provider specifiedProvider;
+
+ /**
* The SPI implementation.
*/
private CipherSpi spiImpl;
/**
+ * The SPI implementation.
+ */
+ private final CipherSpi specifiedSpi;
+
+ /**
* The transformation.
*/
private final String transformation;
@@ -170,8 +180,8 @@
if (!(cipherSpi instanceof NullCipherSpi) && provider == null) {
throw new NullPointerException("provider == null");
}
- this.provider = provider;
- this.spiImpl = cipherSpi;
+ this.specifiedProvider = provider;
+ this.specifiedSpi = cipherSpi;
this.transformation = transformation;
this.transformParts = null;
}
@@ -179,7 +189,8 @@
private Cipher(String transformation, String[] transformParts, Provider provider) {
this.transformation = transformation;
this.transformParts = transformParts;
- this.provider = provider;
+ this.specifiedProvider = provider;
+ this.specifiedSpi = null;
}
@@ -243,7 +254,8 @@
}
/**
- * Creates a new cipher for the specified transformation.
+ * Creates a new cipher for the specified transformation. The
+ * {@code provider} supplied does not have to be registered.
*
* @param transformation
* the name of the transformation to create a cipher for.
@@ -330,12 +342,17 @@
* Makes sure a CipherSpi that matches this type is selected.
*/
private CipherSpi getSpi(Key key) {
+ if (specifiedSpi != null) {
+ return specifiedSpi;
+ }
+
synchronized (initLock) {
- if (spiImpl != null) {
+ if (spiImpl != null && key == null) {
return spiImpl;
}
- final Engine.SpiAndProvider sap = tryCombinations(key, provider, transformParts);
+ final Engine.SpiAndProvider sap = tryCombinations(key, specifiedProvider,
+ transformParts);
if (sap == null) {
throw new ProviderException("No provider for " + transformation);
}
@@ -397,50 +414,65 @@
private static Engine.SpiAndProvider tryTransform(Key key, Provider provider, String transform,
String[] transformParts, NeedToSet type) {
- Engine.SpiAndProvider sap;
- ArrayList<Provider.Service> services = ENGINE.getServices(transform, provider);
+ if (provider != null) {
+ Provider.Service service = provider.getService(SERVICE, transform);
+ if (service == null) {
+ return null;
+ }
+ return tryTransformWithProvider(key, transformParts, type, service);
+ }
+ ArrayList<Provider.Service> services = ENGINE.getServices(transform);
if (services == null) {
return null;
}
for (Provider.Service service : services) {
- try {
- if (key != null && !service.supportsParameter(key)) {
- continue;
- }
-
- /*
- * Check to see if the Cipher even supports the attributes
- * before trying to instantiate it.
- */
- if (!matchAttribute(service, ATTRIBUTE_MODES, transformParts[1])
- || !matchAttribute(service, ATTRIBUTE_PADDINGS, transformParts[2])) {
- continue;
- }
-
- sap = ENGINE.getInstance(service, null);
- if (sap.spi == null || sap.provider == null) {
- continue;
- }
- if (!(sap.spi instanceof CipherSpi)) {
- continue;
- }
- CipherSpi spi = (CipherSpi) sap.spi;
- if (((type == NeedToSet.MODE) || (type == NeedToSet.BOTH))
- && (transformParts[1] != null)) {
- spi.engineSetMode(transformParts[1]);
- }
- if (((type == NeedToSet.PADDING) || (type == NeedToSet.BOTH))
- && (transformParts[2] != null)) {
- spi.engineSetPadding(transformParts[2]);
- }
+ Engine.SpiAndProvider sap = tryTransformWithProvider(key, transformParts, type, service);
+ if (sap != null) {
return sap;
- } catch (NoSuchAlgorithmException ignored) {
- } catch (NoSuchPaddingException ignored) {
}
}
return null;
}
+ private static Engine.SpiAndProvider tryTransformWithProvider(Key key, String[] transformParts,
+ NeedToSet type, Provider.Service service) {
+ try {
+ if (key != null && !service.supportsParameter(key)) {
+ return null;
+ }
+
+ /*
+ * Check to see if the Cipher even supports the attributes before
+ * trying to instantiate it.
+ */
+ if (!matchAttribute(service, ATTRIBUTE_MODES, transformParts[1])
+ || !matchAttribute(service, ATTRIBUTE_PADDINGS, transformParts[2])) {
+ return null;
+ }
+
+ Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
+ if (sap.spi == null || sap.provider == null) {
+ return null;
+ }
+ if (!(sap.spi instanceof CipherSpi)) {
+ return null;
+ }
+ CipherSpi spi = (CipherSpi) sap.spi;
+ if (((type == NeedToSet.MODE) || (type == NeedToSet.BOTH))
+ && (transformParts[1] != null)) {
+ spi.engineSetMode(transformParts[1]);
+ }
+ if (((type == NeedToSet.PADDING) || (type == NeedToSet.BOTH))
+ && (transformParts[2] != null)) {
+ spi.engineSetPadding(transformParts[2]);
+ }
+ return sap;
+ } catch (NoSuchAlgorithmException ignored) {
+ } catch (NoSuchPaddingException ignored) {
+ }
+ return null;
+ }
+
/**
* If the attribute listed exists, check that it matches the regular
* expression.
@@ -459,7 +491,7 @@
/**
* Returns the provider of this cipher instance.
- *
+ *
* @return the provider of this cipher instance.
*/
public final Provider getProvider() {
diff --git a/luni/src/main/java/javax/crypto/ExemptionMechanism.java b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
index 8745b78..c2d42e6 100644
--- a/luni/src/main/java/javax/crypto/ExemptionMechanism.java
+++ b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
@@ -142,6 +142,7 @@
/**
* Returns a new {@code ExemptionMechanism} instance that provides the
* specified exemption mechanism algorithm from the specified provider.
+ * The {@code provider} supplied does not have to be registered.
*
* @param algorithm
* the name of the requested exemption mechanism.
diff --git a/luni/src/main/java/javax/crypto/KeyAgreement.java b/luni/src/main/java/javax/crypto/KeyAgreement.java
index 51b4cd1..abcfd0e 100644
--- a/luni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/luni/src/main/java/javax/crypto/KeyAgreement.java
@@ -23,9 +23,11 @@
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
+import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
import org.apache.harmony.security.fortress.Engine;
/**
@@ -35,22 +37,33 @@
*/
public class KeyAgreement {
+ // The service name.
+ private static final String SERVICE = "KeyAgreement";
+
// Used to access common engine functionality
- private static final Engine ENGINE = new Engine("KeyAgreement");
+ private static final Engine ENGINE = new Engine(SERVICE);
// Store SecureRandom
private static final SecureRandom RANDOM = new SecureRandom();
// Store used provider
- private final Provider provider;
+ private Provider provider;
+
+ // Provider that was requested during creation.
+ private final Provider specifiedProvider;
// Store used spi implementation
- private final KeyAgreementSpi spiImpl;
+ private KeyAgreementSpi spiImpl;
// Store used algorithm name
private final String algorithm;
/**
+ * Lock held while the SPI is initializing.
+ */
+ private final Object initLock = new Object();
+
+ /**
* Creates a new {@code KeyAgreement} instance.
*
* @param keyAgreeSpi
@@ -62,9 +75,9 @@
*/
protected KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider,
String algorithm) {
- this.provider = provider;
- this.algorithm = algorithm;
this.spiImpl = keyAgreeSpi;
+ this.specifiedProvider = provider;
+ this.algorithm = algorithm;
}
/**
@@ -82,6 +95,7 @@
* @return the provider for this {@code KeyAgreement} instance.
*/
public final Provider getProvider() {
+ getSpi();
return provider;
}
@@ -96,13 +110,8 @@
* @throws NullPointerException
* if the specified algorithm is {@code null}.
*/
- public static final KeyAgreement getInstance(String algorithm)
- throws NoSuchAlgorithmException {
- if (algorithm == null) {
- throw new NullPointerException("algorithm == null");
- }
- Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
- return new KeyAgreement((KeyAgreementSpi) sap.spi, sap.provider, algorithm);
+ public static final KeyAgreement getInstance(String algorithm) throws NoSuchAlgorithmException {
+ return getKeyAgreement(algorithm, null);
}
/**
@@ -124,9 +133,8 @@
* @throws IllegalArgumentException
* if the specified provider name is {@code null} or empty.
*/
- public static final KeyAgreement getInstance(String algorithm,
- String provider) throws NoSuchAlgorithmException,
- NoSuchProviderException {
+ public static final KeyAgreement getInstance(String algorithm, String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
throw new IllegalArgumentException("Provider is null or empty");
}
@@ -134,12 +142,13 @@
if (impProvider == null) {
throw new NoSuchProviderException(provider);
}
- return getInstance(algorithm, impProvider);
+ return getKeyAgreement(algorithm, impProvider);
}
/**
* Create a new {@code KeyAgreement} for the specified algorithm from the
- * specified provider.
+ * specified provider. The {@code provider} supplied does not have to be
+ * registered.
*
* @param algorithm
* the name of the key agreement algorithm to create.
@@ -155,29 +164,108 @@
* @throws NullPointerException
* if the specified algorithm name is {@code null}.
*/
- public static final KeyAgreement getInstance(String algorithm,
- Provider provider) throws NoSuchAlgorithmException {
+ public static final KeyAgreement getInstance(String algorithm, Provider provider)
+ throws NoSuchAlgorithmException {
if (provider == null) {
throw new IllegalArgumentException("provider == null");
}
+ return getKeyAgreement(algorithm, provider);
+ }
+
+ private static KeyAgreement getKeyAgreement(String algorithm, Provider provider)
+ throws NoSuchAlgorithmException {
if (algorithm == null) {
throw new NullPointerException("algorithm == null");
}
- Object spi = ENGINE.getInstance(algorithm, provider, null);
- return new KeyAgreement((KeyAgreementSpi) spi, provider, algorithm);
+
+ if (tryAlgorithm(null, provider, algorithm) == null) {
+ if (provider == null) {
+ throw new NoSuchAlgorithmException("No provider found for " + algorithm);
+ } else {
+ throw new NoSuchAlgorithmException("Provider " + provider.getName()
+ + " does not provide " + algorithm);
+ }
+ }
+ return new KeyAgreement(null, provider, algorithm);
+ }
+
+ private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
+ if (provider != null) {
+ Provider.Service service = provider.getService(SERVICE, algorithm);
+ if (service == null) {
+ return null;
+ }
+ return tryAlgorithmWithProvider(key, service);
+ }
+ ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
+ if (services == null) {
+ return null;
+ }
+ for (Provider.Service service : services) {
+ Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
+ if (sap != null) {
+ return sap;
+ }
+ }
+ return null;
+ }
+
+ private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
+ try {
+ if (key != null && !service.supportsParameter(key)) {
+ return null;
+ }
+
+ Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
+ if (sap.spi == null || sap.provider == null) {
+ return null;
+ }
+ if (!(sap.spi instanceof KeyAgreementSpi)) {
+ return null;
+ }
+ return sap;
+ } catch (NoSuchAlgorithmException ignored) {
+ }
+ return null;
+ }
+
+ /**
+ * Makes sure a KeyAgreementSpi that matches this type is selected.
+ */
+ private KeyAgreementSpi getSpi(Key key) {
+ synchronized (initLock) {
+ if (spiImpl != null && key == null) {
+ return spiImpl;
+ }
+
+ final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
+ if (sap == null) {
+ throw new ProviderException("No provider for " + getAlgorithm());
+ }
+
+ spiImpl = (KeyAgreementSpi) sap.spi;
+ provider = sap.provider;
+
+ return spiImpl;
+ }
+ }
+
+ /**
+ * Convenience call when the Key is not available.
+ */
+ private KeyAgreementSpi getSpi() {
+ return getSpi(null);
}
/**
* Initializes this {@code KeyAgreement} with the specified key.
*
- * @param key
- * the key to initialize this key agreement.
- * @throws InvalidKeyException
- * if the specified key cannot be used to initialize this key
- * agreement.
+ * @param key the key to initialize this key agreement.
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize this key agreement.
*/
public final void init(Key key) throws InvalidKeyException {
- spiImpl.engineInit(key, RANDOM);//new SecureRandom());
+ getSpi(key).engineInit(key, RANDOM);//new SecureRandom());
}
/**
@@ -194,7 +282,7 @@
*/
public final void init(Key key, SecureRandom random)
throws InvalidKeyException {
- spiImpl.engineInit(key, random);
+ getSpi(key).engineInit(key, random);
}
/**
@@ -214,7 +302,7 @@
*/
public final void init(Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
- spiImpl.engineInit(key, params, RANDOM);//new SecureRandom());
+ getSpi(key).engineInit(key, params, RANDOM);//new SecureRandom());
}
/**
@@ -237,7 +325,7 @@
public final void init(Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
- spiImpl.engineInit(key, params, random);
+ getSpi(key).engineInit(key, params, random);
}
/**
@@ -259,7 +347,7 @@
*/
public final Key doPhase(Key key, boolean lastPhase)
throws InvalidKeyException, IllegalStateException {
- return spiImpl.engineDoPhase(key, lastPhase);
+ return getSpi().engineDoPhase(key, lastPhase);
}
/**
@@ -270,7 +358,7 @@
* if this key agreement is not complete.
*/
public final byte[] generateSecret() throws IllegalStateException {
- return spiImpl.engineGenerateSecret();
+ return getSpi().engineGenerateSecret();
}
/**
@@ -289,7 +377,7 @@
*/
public final int generateSecret(byte[] sharedSecret, int offset)
throws IllegalStateException, ShortBufferException {
- return spiImpl.engineGenerateSecret(sharedSecret, offset);
+ return getSpi().engineGenerateSecret(sharedSecret, offset);
}
/**
@@ -311,7 +399,7 @@
public final SecretKey generateSecret(String algorithm)
throws IllegalStateException, NoSuchAlgorithmException,
InvalidKeyException {
- return spiImpl.engineGenerateSecret(algorithm);
+ return getSpi().engineGenerateSecret(algorithm);
}
}
diff --git a/luni/src/main/java/javax/crypto/KeyGenerator.java b/luni/src/main/java/javax/crypto/KeyGenerator.java
index 606998a..fc409da 100644
--- a/luni/src/main/java/javax/crypto/KeyGenerator.java
+++ b/luni/src/main/java/javax/crypto/KeyGenerator.java
@@ -137,7 +137,8 @@
/**
* Creates a new {@code KeyGenerator} instance that provides the specified
- * key algorithm from the specified provider.
+ * key algorithm from the specified provider. The {@code provider}
+ * supplied does not have to be registered.
*
* @param algorithm
* the name of the requested key algorithm.
diff --git a/luni/src/main/java/javax/crypto/Mac.java b/luni/src/main/java/javax/crypto/Mac.java
index c208456..5a73dc5 100644
--- a/luni/src/main/java/javax/crypto/Mac.java
+++ b/luni/src/main/java/javax/crypto/Mac.java
@@ -24,8 +24,10 @@
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
+import java.security.ProviderException;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
import org.apache.harmony.security.fortress.Engine;
@@ -35,18 +37,29 @@
*/
public class Mac implements Cloneable {
+ // The service name.
+ private static final String SERVICE = "Mac";
+
//Used to access common engine functionality
- private static final Engine ENGINE = new Engine("Mac");
+ private static final Engine ENGINE = new Engine(SERVICE);
// Store used provider
- private final Provider provider;
+ private Provider provider;
+
+ // Provider that was requested during creation.
+ private final Provider specifiedProvider;
// Store used spi implementation
- private final MacSpi spiImpl;
+ private MacSpi spiImpl;
// Store used algorithm name
private final String algorithm;
+ /**
+ * Lock held while the SPI is initializing.
+ */
+ private final Object initLock = new Object();
+
// Store Mac state (initialized or not initialized)
private boolean isInitMac;
@@ -61,7 +74,7 @@
* the name of the MAC algorithm.
*/
protected Mac(MacSpi macSpi, Provider provider, String algorithm) {
- this.provider = provider;
+ this.specifiedProvider = provider;
this.algorithm = algorithm;
this.spiImpl = macSpi;
this.isInitMac = false;
@@ -82,6 +95,7 @@
* @return the provider of this {@code Mac} instance.
*/
public final Provider getProvider() {
+ getSpi();
return provider;
}
@@ -100,11 +114,7 @@
*/
public static final Mac getInstance(String algorithm)
throws NoSuchAlgorithmException {
- if (algorithm == null) {
- throw new NullPointerException("algorithm == null");
- }
- Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
- return new Mac((MacSpi) sap.spi, sap.provider, algorithm);
+ return getMac(algorithm, null);
}
/**
@@ -136,12 +146,13 @@
if (impProvider == null) {
throw new NoSuchProviderException(provider);
}
- return getInstance(algorithm, impProvider);
+ return getMac(algorithm, impProvider);
}
/**
* Creates a new {@code Mac} instance that provides the specified MAC
- * algorithm from the specified provider.
+ * algorithm from the specified provider. The {@code provider} supplied
+ * does not have to be registered.
*
* @param algorithm
* the name of the requested MAC algorithm.
@@ -162,11 +173,102 @@
if (provider == null) {
throw new IllegalArgumentException("provider == null");
}
+ return getMac(algorithm, provider);
+ }
+
+ private static Mac getMac(String algorithm, Provider provider)
+ throws NoSuchAlgorithmException {
if (algorithm == null) {
throw new NullPointerException("algorithm == null");
}
- Object spi = ENGINE.getInstance(algorithm, provider, null);
- return new Mac((MacSpi) spi, provider, algorithm);
+
+ if (tryAlgorithm(null, provider, algorithm) == null) {
+ if (provider == null) {
+ throw new NoSuchAlgorithmException("No provider found for " + algorithm);
+ } else {
+ throw new NoSuchAlgorithmException("Provider " + provider.getName()
+ + " does not provide " + algorithm);
+ }
+ }
+ return new Mac(null, provider, algorithm);
+ }
+
+ private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
+ if (provider != null) {
+ Provider.Service service = provider.getService(SERVICE, algorithm);
+ if (service == null) {
+ return null;
+ }
+ return tryAlgorithmWithProvider(key, service);
+ }
+ ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
+ if (services == null) {
+ return null;
+ }
+ for (Provider.Service service : services) {
+ Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
+ if (sap != null) {
+ return sap;
+ }
+ }
+ return null;
+ }
+
+ private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
+ try {
+ if (key != null && !service.supportsParameter(key)) {
+ return null;
+ }
+
+ Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
+ if (sap.spi == null || sap.provider == null) {
+ return null;
+ }
+ if (!(sap.spi instanceof MacSpi)) {
+ return null;
+ }
+ return sap;
+ } catch (NoSuchAlgorithmException ignored) {
+ }
+ return null;
+ }
+
+ /**
+ * Makes sure a MacSpi that matches this type is selected.
+ */
+ private MacSpi getSpi(Key key) {
+ synchronized (initLock) {
+ if (spiImpl != null && provider != null && key == null) {
+ return spiImpl;
+ }
+
+ if (algorithm == null) {
+ return null;
+ }
+
+ final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
+ if (sap == null) {
+ throw new ProviderException("No provider for " + getAlgorithm());
+ }
+
+ /*
+ * Set our Spi if we've never been initialized or if we have the Spi
+ * specified and have a null provider.
+ */
+ if (spiImpl == null || provider != null) {
+ spiImpl = (MacSpi) sap.spi;
+ }
+ provider = sap.provider;
+
+ return spiImpl;
+ }
+ }
+
+ /**
+ * Convenience call when the Key is not available.
+ */
+ private MacSpi getSpi() {
+ return getSpi(null);
}
/**
@@ -175,7 +277,7 @@
* @return the length of this MAC (in bytes).
*/
public final int getMacLength() {
- return spiImpl.engineGetMacLength();
+ return getSpi().engineGetMacLength();
}
/**
@@ -198,7 +300,7 @@
if (key == null) {
throw new InvalidKeyException("key == null");
}
- spiImpl.engineInit(key, params);
+ getSpi(key).engineInit(key, params);
isInitMac = true;
}
@@ -219,7 +321,7 @@
throw new InvalidKeyException("key == null");
}
try {
- spiImpl.engineInit(key, null);
+ getSpi(key).engineInit(key, null);
isInitMac = true;
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
@@ -238,7 +340,7 @@
if (!isInitMac) {
throw new IllegalStateException();
}
- spiImpl.engineUpdate(input);
+ getSpi().engineUpdate(input);
}
/**
@@ -269,7 +371,7 @@
+ " input.length=" + input.length
+ " offset=" + offset + ", len=" + len);
}
- spiImpl.engineUpdate(input, offset, len);
+ getSpi().engineUpdate(input, offset, len);
}
/**
@@ -285,7 +387,7 @@
throw new IllegalStateException();
}
if (input != null) {
- spiImpl.engineUpdate(input, 0, input.length);
+ getSpi().engineUpdate(input, 0, input.length);
}
}
@@ -304,7 +406,7 @@
throw new IllegalStateException();
}
if (input != null) {
- spiImpl.engineUpdate(input);
+ getSpi().engineUpdate(input);
} else {
throw new IllegalArgumentException("input == null");
}
@@ -326,7 +428,7 @@
if (!isInitMac) {
throw new IllegalStateException();
}
- return spiImpl.engineDoFinal();
+ return getSpi().engineDoFinal();
}
/**
@@ -361,11 +463,12 @@
if ((outOffset < 0) || (outOffset >= output.length)) {
throw new ShortBufferException("Incorrect outOffset: " + outOffset);
}
- int t = spiImpl.engineGetMacLength();
+ MacSpi spi = getSpi();
+ int t = spi.engineGetMacLength();
if (t > (output.length - outOffset)) {
throw new ShortBufferException("Output buffer is short. Needed " + t + " bytes.");
}
- byte[] result = spiImpl.engineDoFinal();
+ byte[] result = spi.engineDoFinal();
System.arraycopy(result, 0, output, outOffset, result.length);
}
@@ -389,10 +492,11 @@
if (!isInitMac) {
throw new IllegalStateException();
}
+ MacSpi spi = getSpi();
if (input != null) {
- spiImpl.engineUpdate(input, 0, input.length);
+ spi.engineUpdate(input, 0, input.length);
}
- return spiImpl.engineDoFinal();
+ return spi.engineDoFinal();
}
/**
@@ -403,7 +507,7 @@
* initialized with different parameters.
*/
public final void reset() {
- spiImpl.engineReset();
+ getSpi().engineReset();
}
/**
@@ -415,7 +519,11 @@
*/
@Override
public final Object clone() throws CloneNotSupportedException {
- MacSpi newSpiImpl = (MacSpi)spiImpl.clone();
+ MacSpi newSpiImpl = null;
+ final MacSpi spi = getSpi();
+ if (spi != null) {
+ newSpiImpl = (MacSpi) spi.clone();
+ }
Mac mac = new Mac(newSpiImpl, this.provider, this.algorithm);
mac.isInitMac = this.isInitMac;
return mac;
diff --git a/luni/src/main/java/javax/crypto/SecretKeyFactory.java b/luni/src/main/java/javax/crypto/SecretKeyFactory.java
index 8ab3eb8..9298b8e 100644
--- a/luni/src/main/java/javax/crypto/SecretKeyFactory.java
+++ b/luni/src/main/java/javax/crypto/SecretKeyFactory.java
@@ -143,7 +143,8 @@
/**
* Creates a new {@code SecretKeyFactory} instance for the specified key
- * algorithm from the specified provider.
+ * algorithm from the specified provider. The {@code provider} supplied
+ * does not have to be registered.
*
* @param algorithm
* the name of the key algorithm.
diff --git a/luni/src/main/java/javax/net/ssl/SSLContext.java b/luni/src/main/java/javax/net/ssl/SSLContext.java
index f857c25..efc1947 100644
--- a/luni/src/main/java/javax/net/ssl/SSLContext.java
+++ b/luni/src/main/java/javax/net/ssl/SSLContext.java
@@ -314,16 +314,33 @@
}
/**
- * Initializes this {@code SSLContext} instance. All of the arguments are
- * optional, and the security providers will be searched for the required
- * implementations of the needed algorithms.
+ * Initializes this {@code SSLContext} instance. Three aspects of the context can be configured
+ * during initialization:
+ * <ul>
+ * <li>Providers of key material for key exchange and peer authentication
+ * ({@link KeyManager} instances),</li>
+ * <li>Providers of trust decisions about peers ({@link TrustManager} instances),
+ * </li>
+ * <li>Provider of randomness ({@link SecureRandom} instance).</li>
+ * </ul>
+ *
+ * <p>For each type of {@code KeyManager} or {@code TrustManager} used by this context, only the
+ * first matching instance from {@code km} or {@code tm} will be used. For example, only the
+ * first instance of {@link X509TrustManager} from {@code tm} will be used.
+ *
+ * <p>For any parameter set to {@code null} defaults will be used. In that case, the installed
+ * security providers will be searched for the highest priority implementation of the required
+ * primitives. For {@code km} and {@code tm}, the highest priority implementation
+ * of {@link KeyManagerFactory} and {@link TrustManagerFactory} will be used to obtain the
+ * required types of {@code KeyManager} and {@code TrustManager}. For {@code sr}, the default
+ * {@code SecureRandom} implementation will be used.
*
* @param km
- * the key sources or {@code null}.
+ * the key sources or {@code null} for default.
* @param tm
- * the trust decision sources or {@code null}.
+ * the trust decision sources or {@code null} for default.
* @param sr
- * the randomness source or {@code null.}
+ * the randomness source or {@code null} for default.
* @throws KeyManagementException
* if initializing this instance fails.
*/
diff --git a/luni/src/main/java/javax/net/ssl/SSLEngine.java b/luni/src/main/java/javax/net/ssl/SSLEngine.java
index eff70e9..418dfa7 100644
--- a/luni/src/main/java/javax/net/ssl/SSLEngine.java
+++ b/luni/src/main/java/javax/net/ssl/SSLEngine.java
@@ -443,6 +443,18 @@
public abstract SSLSession getSession();
/**
+ * Returns the SSL session for this engine instance. Does not cause the
+ * handshake to start if it has not already started.
+ *
+ * @return the SSL session for this engine instance.
+ * @since 1.7
+ * @hide
+ */
+ public SSLSession getHandshakeSession() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Returns the SSL cipher suite names that are supported by this engine.
* These cipher suites can be enabled using
* {@link #setEnabledCipherSuites(String[])}.
diff --git a/luni/src/main/java/javax/net/ssl/SSLParameters.java b/luni/src/main/java/javax/net/ssl/SSLParameters.java
index 6694ef2..b8fdc38 100644
--- a/luni/src/main/java/javax/net/ssl/SSLParameters.java
+++ b/luni/src/main/java/javax/net/ssl/SSLParameters.java
@@ -27,6 +27,7 @@
private String[] protocols;
private boolean needClientAuth;
private boolean wantClientAuth;
+ private String endpointIdentificationAlgorithm;
/**
* The default SSLParameters constructor. Cipher suites and
@@ -138,4 +139,28 @@
this.wantClientAuth = wantClientAuth;
this.needClientAuth = false;
}
+
+ /**
+ * Returns a string indicating the endpoint identification algorithm to be
+ * used to identify the remote endpoint.
+ *
+ * @see #setEndpointIdentificationAlgorithm(String)
+ * @since 1.7
+ * @hide
+ */
+ public String getEndpointIdentificationAlgorithm() {
+ return endpointIdentificationAlgorithm;
+ }
+
+ /**
+ * Sets the endpoint identification algorithm to be used to identify the
+ * remote endpoint.
+ *
+ * @see #getEndpointIdentificationAlgorithm()
+ * @since 1.7
+ * @hide
+ */
+ public void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
+ this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
+ }
}
diff --git a/luni/src/main/java/javax/net/ssl/SSLSocket.java b/luni/src/main/java/javax/net/ssl/SSLSocket.java
index 2eafbb5..820b884 100644
--- a/luni/src/main/java/javax/net/ssl/SSLSocket.java
+++ b/luni/src/main/java/javax/net/ssl/SSLSocket.java
@@ -859,13 +859,24 @@
public abstract SSLSession getSession();
/**
- * Registers the specified listener to receive notification on completion of a
- * handshake on this connection.
+ * Returns the {@code SSLSession} for this connection. It will not initiate
+ * the handshake and thus will not block until the handshake has been
+ * established.
*
- * @param listener
- * the listener to register.
- * @throws IllegalArgumentException
- * if {@code listener} is {@code null}.
+ * @return the session object.
+ * @since 1.7
+ * @hide
+ */
+ public SSLSession getHandshakeSession() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Registers the specified listener to receive notification on completion of
+ * a handshake on this connection.
+ *
+ * @param listener the listener to register.
+ * @throws IllegalArgumentException if {@code listener} is {@code null}.
*/
public abstract void addHandshakeCompletedListener(HandshakeCompletedListener listener);
diff --git a/luni/src/main/java/javax/net/ssl/X509ExtendedTrustManager.java b/luni/src/main/java/javax/net/ssl/X509ExtendedTrustManager.java
new file mode 100644
index 0000000..8b398ce
--- /dev/null
+++ b/luni/src/main/java/javax/net/ssl/X509ExtendedTrustManager.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 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 javax.net.ssl;
+
+import java.net.Socket;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+/**
+ * Allows the connection constraints such as hostname verification and algorithm
+ * constraints to be checked along with the checks done in
+ * {@link X509TrustManager}.
+ *
+ * @see SSLParameters#setEndpointIdentificationAlgorithm(String)
+ * @since 1.7
+ * @hide
+ */
+public abstract class X509ExtendedTrustManager implements X509TrustManager {
+ /**
+ * Checks whether the specified certificate chain (partial or complete) can
+ * be validated and is trusted for client authentication for the specified
+ * authentication type.
+ * <p>
+ * If the {@code socket} is supplied, its {@link SSLParameters} will be
+ * checked for endpoint identification.
+ *
+ * @param chain the certificate chain to validate.
+ * @param authType the authentication type used.
+ * @param socket the socket from which to check the {@link SSLParameters}
+ * @throws CertificateException if the certificate chain can't be validated
+ * or isn't trusted.
+ * @throws IllegalArgumentException if the specified certificate chain is
+ * empty or {@code null}, or if the specified authentication
+ * type is {@code null} or an empty string.
+ */
+ public abstract void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
+ throws CertificateException;
+
+ /**
+ * Checks whether the specified certificate chain (partial or complete) can
+ * be validated and is trusted for server authentication for the specified
+ * key exchange algorithm.
+ * <p>
+ * If the {@code socket} is supplied, its {@link SSLParameters} will be
+ * checked for endpoint identification.
+ *
+ * @param chain the certificate chain to validate.
+ * @param authType the authentication type used.
+ * @param socket the socket from which to check the {@link SSLParameters}
+ * @throws CertificateException if the certificate chain can't be validated
+ * or isn't trusted.
+ * @throws IllegalArgumentException if the specified certificate chain is
+ * empty or {@code null}, or if the specified authentication
+ * type is {@code null} or an empty string.
+ */
+ public abstract void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
+ throws CertificateException;
+
+ /**
+ * Checks whether the specified certificate chain (partial or complete) can
+ * be validated and is trusted for client authentication for the specified
+ * authentication type.
+ * <p>
+ * If the {@code engine} is supplied, its {@link SSLParameters} will be
+ * checked for endpoint identification.
+ *
+ * @param chain the certificate chain to validate.
+ * @param authType the authentication type used.
+ * @param engine the engine from which to check the {@link SSLParameters}
+ * @throws CertificateException if the certificate chain can't be validated
+ * or isn't trusted.
+ * @throws IllegalArgumentException if the specified certificate chain is
+ * empty or {@code null}, or if the specified authentication
+ * type is {@code null} or an empty string.
+ */
+ public abstract void checkClientTrusted(X509Certificate[] chain, String authType,
+ SSLEngine engine) throws CertificateException;
+
+ /**
+ * Checks whether the specified certificate chain (partial or complete) can
+ * be validated and is trusted for server authentication for the specified
+ * key exchange algorithm.
+ * <p>
+ * If the {@code engine} is supplied, its {@link SSLParameters} will be
+ * checked for endpoint identification.
+ *
+ * @param chain the certificate chain to validate.
+ * @param authType the authentication type used.
+ * @param engine the engine from which to check the {@link SSLParameters}
+ * @throws CertificateException if the certificate chain can't be validated
+ * or isn't trusted.
+ * @throws IllegalArgumentException if the specified certificate chain is
+ * empty or {@code null}, or if the specified authentication
+ * type is {@code null} or an empty string.
+ */
+ public abstract void checkServerTrusted(X509Certificate[] chain, String authType,
+ SSLEngine engine) throws CertificateException;
+}
diff --git a/luni/src/main/java/libcore/icu/CollationElementIteratorICU.java b/luni/src/main/java/libcore/icu/CollationElementIteratorICU.java
index 5779d17..d9f105d 100644
--- a/luni/src/main/java/libcore/icu/CollationElementIteratorICU.java
+++ b/luni/src/main/java/libcore/icu/CollationElementIteratorICU.java
@@ -37,15 +37,6 @@
* @stable ICU 2.4
*/
public final class CollationElementIteratorICU {
- // public data member -------------------------------------------
-
- /**
- * @stable ICU 2.4
- */
- public static final int NULLORDER = 0xFFFFFFFF;
-
- // public methods -----------------------------------------------
-
/**
* Reset the collation elements to their initial state.
* This will move the 'cursor' to the beginning of the text.
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index 2c94882..f60f427 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -453,6 +453,7 @@
public static native String getCurrencyCode(String countryCode);
public static native String getCurrencyDisplayName(String locale, String currencyCode);
public static native int getCurrencyFractionDigits(String currencyCode);
+ public static native int getCurrencyNumericCode(String currencyCode);
public static native String getCurrencySymbol(String locale, String currencyCode);
public static native String getDisplayCountryNative(String countryCode, String locale);
@@ -473,4 +474,7 @@
public static native String languageTagForLocale(String locale);
static native boolean initLocaleDataNative(String locale, LocaleData result);
+
+ public static native void setDefaultLocale(String locale);
+ public static native String getDefaultLocale();
}
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index f00c30f..49228b3 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -95,7 +95,7 @@
public char percent;
public char perMill;
public char monetarySeparator;
- public char minusSign;
+ public String minusSign;
public String exponentSeparator;
public String infinity;
public String NaN;
diff --git a/luni/src/main/java/libcore/icu/NativeCollation.java b/luni/src/main/java/libcore/icu/NativeCollation.java
index 0373fef..64e0278 100644
--- a/luni/src/main/java/libcore/icu/NativeCollation.java
+++ b/luni/src/main/java/libcore/icu/NativeCollation.java
@@ -23,7 +23,7 @@
public static native void closeCollator(long address);
public static native int compare(long address, String source, String target);
public static native int getAttribute(long address, int type);
- public static native int getCollationElementIterator(long address, String source);
+ public static native long getCollationElementIterator(long address, String source);
public static native String getRules(long address);
public static native byte[] getSortKey(long address, String source);
public static native long openCollator(String locale);
diff --git a/luni/src/main/java/libcore/icu/NativeDecimalFormat.java b/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
index fbb3d10..451dd91 100644
--- a/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
+++ b/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
@@ -156,7 +156,7 @@
this.address = open(pattern, dfs.getCurrencySymbol(),
dfs.getDecimalSeparator(), dfs.getDigit(), dfs.getExponentSeparator(),
dfs.getGroupingSeparator(), dfs.getInfinity(),
- dfs.getInternationalCurrencySymbol(), dfs.getMinusSign(),
+ dfs.getInternationalCurrencySymbol(), dfs.getMinusSignString(),
dfs.getMonetaryDecimalSeparator(), dfs.getNaN(), dfs.getPatternSeparator(),
dfs.getPercent(), dfs.getPerMill(), dfs.getZeroDigit());
this.lastPattern = pattern;
@@ -267,7 +267,7 @@
public void setDecimalFormatSymbols(final DecimalFormatSymbols dfs) {
setDecimalFormatSymbols(this.address, dfs.getCurrencySymbol(), dfs.getDecimalSeparator(),
dfs.getDigit(), dfs.getExponentSeparator(), dfs.getGroupingSeparator(),
- dfs.getInfinity(), dfs.getInternationalCurrencySymbol(), dfs.getMinusSign(),
+ dfs.getInfinity(), dfs.getInternationalCurrencySymbol(), dfs.getMinusSignString(),
dfs.getMonetaryDecimalSeparator(), dfs.getNaN(), dfs.getPatternSeparator(),
dfs.getPercent(), dfs.getPerMill(), dfs.getZeroDigit());
}
@@ -562,6 +562,7 @@
case HALF_EVEN: nativeRoundingMode = 4; break;
case HALF_DOWN: nativeRoundingMode = 5; break;
case HALF_UP: nativeRoundingMode = 6; break;
+ case UNNECESSARY: nativeRoundingMode = 7; break;
default: throw new AssertionError();
}
setRoundingMode(address, nativeRoundingMode, roundingIncrement);
@@ -620,13 +621,13 @@
private static native String getTextAttribute(long addr, int symbol);
private static native long open(String pattern, String currencySymbol,
char decimalSeparator, char digit, String exponentSeparator, char groupingSeparator,
- String infinity, String internationalCurrencySymbol, char minusSign,
+ String infinity, String internationalCurrencySymbol, String minusSign,
char monetaryDecimalSeparator, String nan, char patternSeparator, char percent,
char perMill, char zeroDigit);
private static native Number parse(long addr, String string, ParsePosition position, boolean parseBigDecimal);
private static native void setDecimalFormatSymbols(long addr, String currencySymbol,
char decimalSeparator, char digit, String exponentSeparator, char groupingSeparator,
- String infinity, String internationalCurrencySymbol, char minusSign,
+ String infinity, String internationalCurrencySymbol, String minusSign,
char monetaryDecimalSeparator, String nan, char patternSeparator, char percent,
char perMill, char zeroDigit);
private static native void setSymbol(long addr, int symbol, String str);
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 411337a..238c5bd 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -16,15 +16,21 @@
package libcore.io;
+import android.system.ErrnoException;
+import android.system.StructLinger;
+import android.system.StructPollfd;
+import android.system.StructStat;
+import android.system.StructStatVfs;
+import android.util.MutableLong;
import dalvik.system.BlockGuard;
import dalvik.system.SocketTagger;
import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
-import libcore.util.MutableLong;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* Informs BlockGuard of any activity it should be aware of.
@@ -158,6 +164,11 @@
os.mkdir(path, mode);
}
+ @Override public void mkfifo(String path, int mode) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
+ os.mkfifo(path, mode);
+ }
+
@Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
if ((mode & O_ACCMODE) != O_RDONLY) {
@@ -175,37 +186,47 @@
return os.poll(fds, timeoutMs);
}
- @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+ @Override public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
+ os.posix_fallocate(fd, offset, length);
+ }
+
+ @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.pread(fd, buffer, offset);
}
- @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+ @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.pread(fd, bytes, byteOffset, byteCount, offset);
}
- @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+ @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.pwrite(fd, buffer, offset);
}
- @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+ @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.pwrite(fd, bytes, byteOffset, byteCount, offset);
}
- @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.read(fd, buffer);
}
- @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.read(fd, bytes, byteOffset, byteCount);
}
- @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
+ @Override public String readlink(String path) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onReadFromDisk();
+ return os.readlink(path);
+ }
+
+ @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.readv(fd, buffers, offsets, byteCounts);
}
@@ -273,17 +294,17 @@
os.symlink(oldPath, newPath);
}
- @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.write(fd, buffer);
}
- @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.write(fd, bytes, byteOffset, byteCount);
}
- @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
+ @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.writev(fd, buffers, offsets, byteCounts);
}
diff --git a/luni/src/main/java/libcore/io/ErrnoException.java b/luni/src/main/java/libcore/io/ErrnoException.java
deleted file mode 100644
index f484ce9..0000000
--- a/luni/src/main/java/libcore/io/ErrnoException.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 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.IOException;
-import java.net.SocketException;
-
-/**
- * A checked exception thrown when {@link Os} methods fail. This exception contains the native
- * errno value, for comparison against the constants in {@link OsConstants}, should sophisticated
- * callers need to adjust their behavior based on the exact failure.
- */
-public final class ErrnoException extends Exception {
- private final String functionName;
- public final int errno;
-
- public ErrnoException(String functionName, int errno) {
- this.functionName = functionName;
- this.errno = errno;
- }
-
- public ErrnoException(String functionName, int errno, Throwable cause) {
- super(cause);
- this.functionName = functionName;
- this.errno = errno;
- }
-
- /**
- * Converts the stashed function name and errno value to a human-readable string.
- * We do this here rather than in the constructor so that callers only pay for
- * this if they need it.
- */
- @Override public String getMessage() {
- String errnoName = OsConstants.errnoName(errno);
- if (errnoName == null) {
- errnoName = "errno " + errno;
- }
- String description = Libcore.os.strerror(errno);
- return functionName + " failed: " + errnoName + " (" + description + ")";
- }
-
- public IOException rethrowAsIOException() throws IOException {
- IOException newException = new IOException(getMessage());
- newException.initCause(this);
- throw newException;
- }
-
- public SocketException rethrowAsSocketException() throws SocketException {
- throw new SocketException(getMessage(), this);
- }
-}
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 3800416..9a993c5 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -16,14 +16,29 @@
package libcore.io;
+import android.system.ErrnoException;
+import android.system.GaiException;
+import android.system.StructAddrinfo;
+import android.system.StructFlock;
+import android.system.StructGroupReq;
+import android.system.StructGroupSourceReq;
+import android.system.StructLinger;
+import android.system.StructPasswd;
+import android.system.StructPollfd;
+import android.system.StructStat;
+import android.system.StructStatVfs;
+import android.system.StructTimeval;
+import android.system.StructUcred;
+import android.system.StructUtsname;
+import android.util.MutableInt;
+import android.util.MutableLong;
import java.io.FileDescriptor;
+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 libcore.util.MutableInt;
-import libcore.util.MutableLong;
/**
* Subclass this if you want to override some {@link Os} methods but otherwise delegate.
@@ -90,6 +105,7 @@
public StructStat lstat(String path) throws ErrnoException { return os.lstat(path); }
public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException { os.mincore(address, byteCount, vector); }
public void mkdir(String path, int mode) throws ErrnoException { os.mkdir(path, mode); }
+ public void mkfifo(String path, int mode) throws ErrnoException { os.mkfifo(path, mode); }
public void mlock(long address, long byteCount) throws ErrnoException { os.mlock(address, byteCount); }
public long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException { return os.mmap(address, byteCount, prot, flags, fd, offset); }
public void msync(long address, long byteCount, int flags) throws ErrnoException { os.msync(address, byteCount, flags); }
@@ -98,13 +114,16 @@
public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { return os.open(path, flags, mode); }
public FileDescriptor[] pipe() throws ErrnoException { return os.pipe(); }
public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return os.poll(fds, timeoutMs); }
- public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException { return os.pread(fd, buffer, offset); }
- public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException { return os.pread(fd, bytes, byteOffset, byteCount, offset); }
- public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException { return os.pwrite(fd, buffer, offset); }
- public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException { return os.pwrite(fd, bytes, byteOffset, byteCount, offset); }
- public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.read(fd, buffer); }
- public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.read(fd, bytes, byteOffset, byteCount); }
- public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.readv(fd, buffers, offsets, byteCounts); }
+ public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { os.posix_fallocate(fd, offset, length); }
+ public int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException { return os.prctl(option, arg2, arg3, arg4, arg5); };
+ public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return os.pread(fd, buffer, offset); }
+ public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return os.pread(fd, bytes, byteOffset, byteCount, offset); }
+ public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return os.pwrite(fd, buffer, offset); }
+ public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return os.pwrite(fd, bytes, byteOffset, byteCount, offset); }
+ public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.read(fd, buffer); }
+ public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return os.read(fd, bytes, byteOffset, byteCount); }
+ public String readlink(String path) throws ErrnoException { return os.readlink(path); }
+ public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.readv(fd, buffers, offsets, byteCounts); }
public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
public void remove(String path) throws ErrnoException { os.remove(path); }
@@ -122,6 +141,7 @@
public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptInt(fd, level, option, value); }
public void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptIpMreqn(fd, level, option, value); }
public void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException { os.setsockoptGroupReq(fd, level, option, value); }
+ public void setsockoptGroupSourceReq(FileDescriptor fd, int level, int option, StructGroupSourceReq value) throws ErrnoException { os.setsockoptGroupSourceReq(fd, level, option, value); }
public void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException { os.setsockoptLinger(fd, level, option, value); }
public void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException { os.setsockoptTimeval(fd, level, option, value); }
public void setuid(int uid) throws ErrnoException { os.setuid(uid); }
@@ -140,7 +160,7 @@
public StructUtsname uname() { return os.uname(); }
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 { return os.write(fd, buffer); }
- public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.write(fd, bytes, byteOffset, byteCount); }
- public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.writev(fd, buffers, offsets, byteCounts); }
+ public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.write(fd, buffer); }
+ public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return os.write(fd, bytes, byteOffset, byteCount); }
+ public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.writev(fd, buffers, offsets, byteCounts); }
}
diff --git a/luni/src/main/java/libcore/io/GaiException.java b/luni/src/main/java/libcore/io/GaiException.java
deleted file mode 100644
index 08143dc..0000000
--- a/luni/src/main/java/libcore/io/GaiException.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2011 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.net.UnknownHostException;
-import libcore.io.OsConstants;
-
-/**
- * An unchecked exception thrown when the {@link Os} {@code getaddrinfo} or {@code getnameinfo}
- * methods fail. This exception contains the native error value, for comparison against the
- * {@code GAI_} constants in {@link OsConstants}, should sophisticated
- * callers need to adjust their behavior based on the exact failure.
- */
-public final class GaiException extends RuntimeException {
- private final String functionName;
- public final int error;
-
- public GaiException(String functionName, int error) {
- this.functionName = functionName;
- this.error = error;
- }
-
- public GaiException(String functionName, int error, Throwable cause) {
- super(cause);
- this.functionName = functionName;
- this.error = error;
- }
-
- /**
- * Converts the stashed function name and error value to a human-readable string.
- * We do this here rather than in the constructor so that callers only pay for
- * this if they need it.
- */
- @Override public String getMessage() {
- String gaiName = OsConstants.gaiName(error);
- if (gaiName == null) {
- gaiName = "GAI_ error " + error;
- }
- String description = Libcore.os.gai_strerror(error);
- return functionName + " failed: " + gaiName + " (" + description + ")";
- }
-
- public UnknownHostException rethrowAsUnknownHostException(String detailMessage) throws UnknownHostException {
- UnknownHostException newException = new UnknownHostException(detailMessage);
- newException.initCause(this);
- throw newException;
- }
-
- public UnknownHostException rethrowAsUnknownHostException() throws UnknownHostException {
- throw rethrowAsUnknownHostException(getMessage());
- }
-}
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
index e1c4ba6..acc8d4f 100644
--- a/luni/src/main/java/libcore/io/IoBridge.java
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -16,6 +16,13 @@
package libcore.io;
+import android.system.ErrnoException;
+import android.system.StructGroupReq;
+import android.system.StructGroupSourceReq;
+import android.system.StructLinger;
+import android.system.StructPollfd;
+import android.system.StructTimeval;
+import android.util.MutableInt;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -35,8 +42,7 @@
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Arrays;
-import static libcore.io.OsConstants.*;
-import libcore.util.MutableInt;
+import static android.system.OsConstants.*;
/**
* Implements java.io/java.net/java.nio semantics in terms of the underlying POSIX system calls.
@@ -71,16 +77,20 @@
public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
- if (address instanceof Inet6Address && ((Inet6Address) address).getScopeId() == 0) {
- // Linux won't let you bind a link-local address without a scope id. Find one.
- NetworkInterface nif = NetworkInterface.getByInetAddress(address);
- if (nif == null) {
- throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
- }
- try {
- address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
- } catch (UnknownHostException ex) {
- throw new AssertionError(ex); // Can't happen.
+ if (address instanceof Inet6Address) {
+ Inet6Address inet6Address = (Inet6Address) address;
+ if (inet6Address.getScopeId() == 0 && inet6Address.isLinkLocalAddress()) {
+ // Linux won't let you bind a link-local address without a scope id.
+ // Find one.
+ NetworkInterface nif = NetworkInterface.getByInetAddress(address);
+ if (nif == null) {
+ throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
+ }
+ try {
+ address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
+ } catch (UnknownHostException ex) {
+ throw new AssertionError(ex); // Can't happen.
+ }
}
}
try {
@@ -95,9 +105,9 @@
* Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
* means this method won't throw SocketTimeoutException.
*/
- public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
+ public static void connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
try {
- return IoBridge.connect(fd, inetAddress, port, 0);
+ IoBridge.connect(fd, inetAddress, port, 0);
} catch (SocketTimeoutException ex) {
throw new AssertionError(ex); // Can't happen for a connect without a timeout.
}
@@ -107,9 +117,9 @@
* Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
* Use timeoutMs == 0 for a blocking connect with no timeout.
*/
- public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
+ public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
try {
- return connectErrno(fd, inetAddress, port, timeoutMs);
+ connectErrno(fd, inetAddress, port, timeoutMs);
} catch (ErrnoException errnoException) {
throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException);
} catch (SocketException ex) {
@@ -121,11 +131,11 @@
}
}
- private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws ErrnoException, IOException {
+ private static void connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws ErrnoException, IOException {
// With no timeout, just call connect(2) directly.
if (timeoutMs == 0) {
Libcore.os.connect(fd, inetAddress, port);
- return true;
+ return;
}
// For connect with a timeout, we:
@@ -143,7 +153,7 @@
try {
Libcore.os.connect(fd, inetAddress, port);
IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
- return true; // We connected immediately.
+ return; // We connected immediately.
} catch (ErrnoException errnoException) {
if (errnoException.errno != EINPROGRESS) {
throw errnoException;
@@ -160,7 +170,6 @@
}
} while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
- return true; // Or we'd have thrown.
}
private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
@@ -174,9 +183,15 @@
return detail;
}
- public static void closeSocket(FileDescriptor fd) throws IOException {
- if (!fd.valid()) {
- // Socket.close doesn't throw if you try to close an already-closed socket.
+ /**
+ * Closes the supplied file descriptor and sends a signal to any threads are currently blocking.
+ * In order for the signal to be sent the blocked threads must have registered with
+ * the AsynchronousCloseMonitor before they entered the blocking operation.
+ *
+ * <p>This method is a no-op if passed a {@code null} or already-closed file descriptor.
+ */
+ public static void closeAndSignalBlockedThreads(FileDescriptor fd) throws IOException {
+ if (fd == null || !fd.valid()) {
return;
}
int intFd = fd.getInt$();
@@ -226,6 +241,10 @@
// Socket options used by java.net but not exposed in SocketOptions.
public static final int JAVA_MCAST_JOIN_GROUP = 19;
public static final int JAVA_MCAST_LEAVE_GROUP = 20;
+ public static final int JAVA_MCAST_JOIN_SOURCE_GROUP = 21;
+ public static final int JAVA_MCAST_LEAVE_SOURCE_GROUP = 22;
+ public static final int JAVA_MCAST_BLOCK_SOURCE = 23;
+ public static final int JAVA_MCAST_UNBLOCK_SOURCE = 24;
public static final int JAVA_IP_MULTICAST_TTL = 17;
/**
@@ -369,16 +388,46 @@
return;
case IoBridge.JAVA_MCAST_JOIN_GROUP:
case IoBridge.JAVA_MCAST_LEAVE_GROUP:
+ {
StructGroupReq groupReq = (StructGroupReq) value;
int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
return;
+ }
+ case IoBridge.JAVA_MCAST_JOIN_SOURCE_GROUP:
+ case IoBridge.JAVA_MCAST_LEAVE_SOURCE_GROUP:
+ case IoBridge.JAVA_MCAST_BLOCK_SOURCE:
+ case IoBridge.JAVA_MCAST_UNBLOCK_SOURCE:
+ {
+ StructGroupSourceReq groupSourceReq = (StructGroupSourceReq) value;
+ int level = (groupSourceReq.gsr_group instanceof Inet4Address)
+ ? IPPROTO_IP : IPPROTO_IPV6;
+ int op = getGroupSourceReqOp(option);
+ Libcore.os.setsockoptGroupSourceReq(fd, level, op, groupSourceReq);
+ return;
+ }
default:
throw new SocketException("Unknown socket option: " + option);
}
}
+ private static int getGroupSourceReqOp(int javaValue) {
+ switch (javaValue) {
+ case IoBridge.JAVA_MCAST_JOIN_SOURCE_GROUP:
+ return MCAST_JOIN_SOURCE_GROUP;
+ case IoBridge.JAVA_MCAST_LEAVE_SOURCE_GROUP:
+ return MCAST_LEAVE_SOURCE_GROUP;
+ case IoBridge.JAVA_MCAST_BLOCK_SOURCE:
+ return MCAST_BLOCK_SOURCE;
+ case IoBridge.JAVA_MCAST_UNBLOCK_SOURCE:
+ return MCAST_UNBLOCK_SOURCE;
+ default:
+ throw new AssertionError(
+ "Unknown java value for setsocketopt op lookup: " + javaValue);
+ }
+ }
+
/**
* java.io only throws FileNotFoundException when opening files, regardless of what actually
* went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
diff --git a/luni/src/main/java/libcore/io/IoUtils.java b/luni/src/main/java/libcore/io/IoUtils.java
index 10ef671..5a19f17 100644
--- a/luni/src/main/java/libcore/io/IoUtils.java
+++ b/luni/src/main/java/libcore/io/IoUtils.java
@@ -16,6 +16,8 @@
package libcore.io;
+import android.system.ErrnoException;
+import android.system.StructStat;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -25,7 +27,7 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Random;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
public final class IoUtils {
private static final Random TEMPORARY_DIRECTORY_PRNG = new Random();
diff --git a/luni/src/main/java/libcore/io/MemoryMappedFile.java b/luni/src/main/java/libcore/io/MemoryMappedFile.java
index 2d8aa2b..b4cd8fc 100644
--- a/luni/src/main/java/libcore/io/MemoryMappedFile.java
+++ b/luni/src/main/java/libcore/io/MemoryMappedFile.java
@@ -16,16 +16,16 @@
package libcore.io;
+import android.system.ErrnoException;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
-import java.nio.NioUtils;
import java.nio.channels.FileChannel;
-import libcore.io.ErrnoException;
+import java.nio.NioUtils;
import libcore.io.Libcore;
import libcore.io.Memory;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* A memory-mapped file. Use {@link #mmap} to map a file, {@link #close} to unmap a file,
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 2b68027..e55ae5c 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -16,14 +16,29 @@
package libcore.io;
+import android.system.ErrnoException;
+import android.system.GaiException;
+import android.system.StructAddrinfo;
+import android.system.StructFlock;
+import android.system.StructGroupReq;
+import android.system.StructGroupSourceReq;
+import android.system.StructLinger;
+import android.system.StructPasswd;
+import android.system.StructPollfd;
+import android.system.StructStat;
+import android.system.StructStatVfs;
+import android.system.StructTimeval;
+import android.system.StructUcred;
+import android.system.StructUtsname;
+import android.util.MutableInt;
+import android.util.MutableLong;
import java.io.FileDescriptor;
+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 libcore.util.MutableInt;
-import libcore.util.MutableLong;
public interface Os {
public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
@@ -82,6 +97,7 @@
public StructStat lstat(String path) throws ErrnoException;
public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException;
public void mkdir(String path, int mode) throws ErrnoException;
+ public void mkfifo(String path, int mode) throws ErrnoException;
public void mlock(long address, long byteCount) throws ErrnoException;
public long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;
public void msync(long address, long byteCount, int flags) throws ErrnoException;
@@ -91,13 +107,16 @@
public FileDescriptor[] pipe() throws ErrnoException;
/* TODO: if we used the non-standard ppoll(2) behind the scenes, we could take a long timeout. */
public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
- public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException;
- public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException;
- public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException;
- public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException;
- public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
- public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
- public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
+ public int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException;
+ public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException;
+ public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+ public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException;
+ public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+ public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
+ public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException;
+ public String readlink(String path) throws ErrnoException;
+ public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public void remove(String path) throws ErrnoException;
@@ -115,6 +134,7 @@
public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException;
+ public void setsockoptGroupSourceReq(FileDescriptor fd, int level, int option, StructGroupSourceReq value) throws ErrnoException;
public void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException;
public void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException;
public void setuid(int uid) throws ErrnoException;
@@ -133,7 +153,7 @@
public StructUtsname uname();
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;
- public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
- public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
+ public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException;
+ public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
}
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index b99941c..8d99f5e 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -16,15 +16,30 @@
package libcore.io;
+import android.system.ErrnoException;
+import android.system.GaiException;
+import android.system.StructAddrinfo;
+import android.system.StructFlock;
+import android.system.StructGroupReq;
+import android.system.StructGroupSourceReq;
+import android.system.StructLinger;
+import android.system.StructPasswd;
+import android.system.StructPollfd;
+import android.system.StructStat;
+import android.system.StructStatVfs;
+import android.system.StructTimeval;
+import android.system.StructUcred;
+import android.system.StructUtsname;
+import android.util.MutableInt;
+import android.util.MutableLong;
import java.io.FileDescriptor;
+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 java.nio.NioUtils;
-import libcore.util.MutableInt;
-import libcore.util.MutableLong;
public final class Posix implements Os {
Posix() { }
@@ -84,6 +99,7 @@
public native StructStat lstat(String path) throws ErrnoException;
public native void mincore(long address, long byteCount, byte[] vector) throws ErrnoException;
public native void mkdir(String path, int mode) throws ErrnoException;
+ public native void mkfifo(String path, int mode) throws ErrnoException;
public native void mlock(long address, long byteCount) throws ErrnoException;
public native long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;
public native void msync(long address, long byteCount, int flags) throws ErrnoException;
@@ -92,43 +108,46 @@
public native FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
public native FileDescriptor[] pipe() throws ErrnoException;
public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
- public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+ public native void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
+ public native int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException;
+ public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
if (buffer.isDirect()) {
return preadBytes(fd, buffer, buffer.position(), buffer.remaining(), offset);
} else {
return preadBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), offset);
}
}
- public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+ public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return preadBytes(fd, bytes, byteOffset, byteCount, offset);
}
- private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException;
- public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+ private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+ public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
if (buffer.isDirect()) {
return pwriteBytes(fd, buffer, buffer.position(), buffer.remaining(), offset);
} else {
return pwriteBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), offset);
}
}
- public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+ public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return pwriteBytes(fd, bytes, byteOffset, byteCount, offset);
}
- private native int pwriteBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException;
- public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ private native int pwriteBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+ public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
if (buffer.isDirect()) {
return readBytes(fd, buffer, buffer.position(), buffer.remaining());
} else {
return readBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
}
}
- public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return readBytes(fd, bytes, byteOffset, byteCount);
}
- private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
- public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException;
+ public native String readlink(String path) throws ErrnoException;
+ public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
if (buffer.isDirect()) {
return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress);
@@ -166,6 +185,7 @@
public native void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public native void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public native void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException;
+ public native void setsockoptGroupSourceReq(FileDescriptor fd, int level, int option, StructGroupSourceReq value) throws ErrnoException;
public native void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException;
public native void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException;
public native void setuid(int uid) throws ErrnoException;
@@ -190,17 +210,17 @@
public native StructUtsname uname();
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 {
+ public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
if (buffer.isDirect()) {
return writeBytes(fd, buffer, buffer.position(), buffer.remaining());
} else {
return writeBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
}
}
- public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return writeBytes(fd, bytes, byteOffset, byteCount);
}
- private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
- public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException;
+ public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
}
diff --git a/luni/src/main/java/libcore/io/StructAddrinfo.java b/luni/src/main/java/libcore/io/StructAddrinfo.java
deleted file mode 100644
index 8c8181d..0000000
--- a/luni/src/main/java/libcore/io/StructAddrinfo.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2011 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.net.InetAddress;
-
-/**
- * Information returned/taken by getaddrinfo(3). Corresponds to C's {@code struct addrinfo} from
- * <a href="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/netdb.h.html"><netdb.h></a>
- *
- * TODO: we currently only _take_ a StructAddrinfo; getaddrinfo returns an InetAddress[].
- */
-public final class StructAddrinfo {
- /** Flags describing the kind of lookup to be done. (Such as AI_ADDRCONFIG.) */
- public int ai_flags;
-
- /** Desired address family for results. (Such as AF_INET6 for IPv6. AF_UNSPEC means "any".) */
- public int ai_family;
-
- /** Socket type. (Such as SOCK_DGRAM. 0 means "any".) */
- public int ai_socktype;
-
- /** Protocol. (Such as IPPROTO_IPV6 IPv6. 0 means "any".) */
- public int ai_protocol;
-
- /** Address length. (Not useful in Java.) */
- // public int ai_addrlen;
-
- /** Address. */
- public InetAddress ai_addr;
-
- /** Canonical name of service location (if AI_CANONNAME provided in ai_flags). */
- // public String ai_canonname;
-
- /** Next element in linked list. */
- public StructAddrinfo ai_next;
-}
diff --git a/luni/src/main/java/libcore/io/StructPollfd.java b/luni/src/main/java/libcore/io/StructPollfd.java
deleted file mode 100644
index c659d6e..0000000
--- a/luni/src/main/java/libcore/io/StructPollfd.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2011 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.FileDescriptor;
-
-/**
- * Corresponds to C's {@code struct pollfd} from
- * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/poll.h.html"><poll.h></a>
- */
-public final class StructPollfd {
- /** The file descriptor to poll. */
- public FileDescriptor fd;
-
- /**
- * The events we're interested in. POLLIN corresponds to being in select(2)'s read fd set,
- * POLLOUT to the write fd set.
- */
- public short events;
-
- /** The events that actually happened. */
- public short revents;
-
- /**
- * A non-standard extension that lets callers conveniently map back to the object
- * their fd belongs to. This is used by Selector, for example, to associate each
- * FileDescriptor with the corresponding SelectionKey.
- */
- public Object userData;
-
- @Override public String toString() {
- return "StructPollfd[fd=" + fd + ",events=" + events + ",revents=" + revents + "]";
- }
-}
diff --git a/luni/src/main/java/libcore/io/StructStat.java b/luni/src/main/java/libcore/io/StructStat.java
deleted file mode 100644
index 05ecca7..0000000
--- a/luni/src/main/java/libcore/io/StructStat.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-/**
- * File information returned by fstat(2), lstat(2), and stat(2). Corresponds to C's
- * {@code struct stat} from
- * <a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/stat.h.html"><stat.h></a>
- */
-public final class StructStat {
- /** Device ID of device containing file. */
- public final long st_dev; /*dev_t*/
-
- /** File serial number (inode). */
- public final long st_ino; /*ino_t*/
-
- /** Mode (permissions) of file. */
- public final int st_mode; /*mode_t*/
-
- /** Number of hard links to the file. */
- public final long st_nlink; /*nlink_t*/
-
- /** User ID of file. */
- public final int st_uid; /*uid_t*/
-
- /** Group ID of file. */
- public final int st_gid; /*gid_t*/
-
- /** Device ID (if file is character or block special). */
- public final long st_rdev; /*dev_t*/
-
- /**
- * For regular files, the file size in bytes.
- * For symbolic links, the length in bytes of the pathname contained in the symbolic link.
- * For a shared memory object, the length in bytes.
- * For a typed memory object, the length in bytes.
- * For other file types, the use of this field is unspecified.
- */
- public final long st_size; /*off_t*/
-
- /** Time of last access. */
- public final long st_atime; /*time_t*/
-
- /** Time of last data modification. */
- public final long st_mtime; /*time_t*/
-
- /** Time of last status change. */
- public final long st_ctime; /*time_t*/
-
- /**
- * A file system-specific preferred I/O block size for this object.
- * For some file system types, this may vary from file to file.
- */
- public final long st_blksize; /*blksize_t*/
-
- /** Number of blocks allocated for this object. */
- public final long st_blocks; /*blkcnt_t*/
-
- StructStat(long st_dev, long st_ino, int st_mode, long st_nlink, int st_uid, int st_gid,
- long st_rdev, long st_size, long st_atime, long st_mtime, long st_ctime,
- long st_blksize, long st_blocks) {
- this.st_dev = st_dev;
- this.st_ino = st_ino;
- this.st_mode = st_mode;
- this.st_nlink = st_nlink;
- this.st_uid = st_uid;
- this.st_gid = st_gid;
- this.st_rdev = st_rdev;
- this.st_size = st_size;
- this.st_atime = st_atime;
- this.st_mtime = st_mtime;
- this.st_ctime = st_ctime;
- this.st_blksize = st_blksize;
- this.st_blocks = st_blocks;
- }
-}
diff --git a/luni/src/main/java/libcore/io/StructTimeval.java b/luni/src/main/java/libcore/io/StructTimeval.java
deleted file mode 100644
index 0ed3509..0000000
--- a/luni/src/main/java/libcore/io/StructTimeval.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-/**
- * Corresponds to C's {@code struct timeval} from
- * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html"><sys/time.h></a>
- */
-public final class StructTimeval {
- /** Seconds. */
- public final long tv_sec;
-
- /** Microseconds. */
- public final long tv_usec;
-
- private StructTimeval(long tv_sec, long tv_usec) {
- this.tv_sec = tv_sec;
- this.tv_usec = tv_usec;
- }
-
- public static StructTimeval fromMillis(long millis) {
- long tv_sec = millis / 1000;
- long tv_usec = (millis - (tv_sec * 1000)) * 1000;
- return new StructTimeval(tv_sec, tv_usec);
- }
-
- public long toMillis() {
- return (tv_sec * 1000) + (tv_usec / 1000);
- }
-
- @Override public String toString() {
- return "StructTimeval[tv_sec=" + tv_sec + ",tv_usec=" + tv_usec + "]";
- }
-}
diff --git a/luni/src/main/java/libcore/io/StructUtsname.java b/luni/src/main/java/libcore/io/StructUtsname.java
deleted file mode 100644
index e6a8e42..0000000
--- a/luni/src/main/java/libcore/io/StructUtsname.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-/**
- * Information returned by uname(2). Corresponds to C's
- * {@code struct utsname} from
- * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_utsname.h.html"><sys/utsname.h></a>
- */
-public final class StructUtsname {
- /** The OS name, such as "Linux". */
- public final String sysname;
-
- /** The machine's unqualified name on some implementation-defined network. */
- public final String nodename;
-
- /** The OS release, such as "2.6.35-27-generic". */
- public final String release;
-
- /** The OS version, such as "#48-Ubuntu SMP Tue Feb 22 20:25:29 UTC 2011". */
- public final String version;
-
- /** The machine architecture, such as "armv7l" or "x86_64". */
- public final String machine;
-
- StructUtsname(String sysname, String nodename, String release, String version, String machine) {
- this.sysname = sysname;
- this.nodename = nodename;
- this.release = release;
- this.version = version;
- this.machine = machine;
- }
-}
diff --git a/luni/src/main/java/libcore/net/url/FileURLConnection.java b/luni/src/main/java/libcore/net/url/FileURLConnection.java
index b4654cd..f8d7926 100644
--- a/luni/src/main/java/libcore/net/url/FileURLConnection.java
+++ b/luni/src/main/java/libcore/net/url/FileURLConnection.java
@@ -42,7 +42,7 @@
private InputStream is;
- private int length = -1;
+ private long length = -1;
private boolean isDir;
@@ -80,21 +80,31 @@
// use -1 for the contentLength
} else {
is = new BufferedInputStream(new FileInputStream(f));
- long lengthAsLong = f.length();
- length = lengthAsLong <= Integer.MAX_VALUE ? (int) lengthAsLong : Integer.MAX_VALUE;
+ length = f.length();
}
connected = true;
}
/**
+ * Returns the length of the file in bytes, or {@code -1} if the length cannot be
+ * represented as an {@code int}. See {@link #getContentLengthLong()} for a method that can
+ * handle larger files.
+ */
+ @Override
+ public int getContentLength() {
+ long length = getContentLengthLong();
+ return length <= Integer.MAX_VALUE ? (int) length : -1;
+ }
+
+ /**
* Returns the length of the file in bytes.
*
* @return the length of the file
- *
- * @see #getContentType()
+ * @since 1.7
+ * @hide Until ready for a public API change
*/
@Override
- public int getContentLength() {
+ public long getContentLengthLong() {
try {
if (!connected) {
connect();
diff --git a/luni/src/main/java/libcore/net/url/JarURLConnectionImpl.java b/luni/src/main/java/libcore/net/url/JarURLConnectionImpl.java
index 762f0e2..e00bcab 100644
--- a/luni/src/main/java/libcore/net/url/JarURLConnectionImpl.java
+++ b/luni/src/main/java/libcore/net/url/JarURLConnectionImpl.java
@@ -258,21 +258,32 @@
}
/**
- * Returns the content length of the resource. Test cases reveal that if the
- * URL is referring to a Jar file, this method answers a content-length
- * returned by URLConnection. For jar entry it should return it's size.
- * Otherwise, it will return -1.
- *
- * @return the content length
+ * Returns the content length of the resource. Test cases reveal that if the URL is referring to
+ * a Jar file, this method answers a content-length returned by URLConnection. For a jar entry
+ * it returns the entry's size if it can be represented as an {@code int}. Otherwise, it will
+ * return -1.
*/
@Override
public int getContentLength() {
+ long length = getContentLengthLong();
+ return length > Integer.MAX_VALUE ? -1 : (int) length;
+ }
+
+ /**
+ * Returns the content length of the resource. Test cases reveal that if the URL is referring to
+ * a Jar file, this method answers a content-length returned by URLConnection. For a jar entry
+ * it should return the entry's size. Otherwise, it will return -1.
+ *
+ * @hide Until ready for a public API change
+ */
+ @Override
+ public long getContentLengthLong() {
try {
connect();
if (jarEntry == null) {
- return jarFileURLConnection.getContentLength();
+ return jarFileURLConnection.getContentLengthLong();
}
- return (int) getJarEntry().getSize();
+ return getJarEntry().getSize();
} catch (IOException e) {
// Ignored
}
diff --git a/luni/src/main/java/libcore/reflect/AnnotationAccess.java b/luni/src/main/java/libcore/reflect/AnnotationAccess.java
index 6ff82c4..d63ad30 100644
--- a/luni/src/main/java/libcore/reflect/AnnotationAccess.java
+++ b/luni/src/main/java/libcore/reflect/AnnotationAccess.java
@@ -183,6 +183,11 @@
private static com.android.dex.Annotation getMethodAnnotation(
AnnotatedElement element, Class<? extends Annotation> annotationClass) {
+ int annotationSetOffset = getAnnotationSetOffset(element);
+ if (annotationSetOffset == 0) {
+ return null; // no annotation
+ }
+
Class<?> dexClass = getDexClass(element);
Dex dex = dexClass.getDex();
int annotationTypeIndex = getTypeIndex(dex, annotationClass);
@@ -190,11 +195,6 @@
return null; // The dex file doesn't use this annotation.
}
- int annotationSetOffset = getAnnotationSetOffset(element);
- if (annotationSetOffset == 0) {
- return null; // no annotation
- }
-
Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item
for (int i = 0, size = setIn.readInt(); i < size; i++) {
int annotationOffset = setIn.readInt();
@@ -228,19 +228,22 @@
int methodsSize = directoryIn.readInt();
directoryIn.readInt(); // parameters size
- int fieldIndex = element instanceof Field ? ((Field) element).getDexFieldIndex() : -1;
- for (int i = 0; i < fieldsSize; i++) {
- int candidateFieldIndex = directoryIn.readInt();
- int annotationSetOffset = directoryIn.readInt();
- if (candidateFieldIndex == fieldIndex) {
- return annotationSetOffset;
- }
- }
- // we must read all fields prior to methods, if we were searching for a field then we missed
if (element instanceof Field) {
+ int fieldIndex = ((Field) element).getDexFieldIndex();
+ for (int i = 0; i < fieldsSize; i++) {
+ int candidateFieldIndex = directoryIn.readInt();
+ int annotationSetOffset = directoryIn.readInt();
+ if (candidateFieldIndex == fieldIndex) {
+ return annotationSetOffset;
+ }
+ }
+ // if we were searching for a field then we missed
return 0;
}
+ // Skip through the fields without reading them and look for constructors or methods.
+ directoryIn.skip(8 * fieldsSize);
+
int methodIndex= element instanceof Method ? ((Method) element).getDexMethodIndex()
: ((Constructor<?>) element).getDexMethodIndex();
for (int i = 0; i < methodsSize; i++) {
diff --git a/luni/src/main/java/libcore/util/EmptyArray.java b/luni/src/main/java/libcore/util/EmptyArray.java
index 1713bfc..eb91589c 100644
--- a/luni/src/main/java/libcore/util/EmptyArray.java
+++ b/luni/src/main/java/libcore/util/EmptyArray.java
@@ -24,6 +24,7 @@
public static final char[] CHAR = new char[0];
public static final double[] DOUBLE = new double[0];
public static final int[] INT = new int[0];
+ public static final long[] LONG = new long[0];
public static final Class<?>[] CLASS = new Class[0];
public static final Object[] OBJECT = new Object[0];
diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java
index 10e3900..7ff377c 100644
--- a/luni/src/main/java/libcore/util/ZoneInfoDB.java
+++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java
@@ -16,6 +16,7 @@
package libcore.util;
+import android.system.ErrnoException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
@@ -27,7 +28,6 @@
import java.util.List;
import java.util.TimeZone;
import libcore.io.BufferIterator;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.MemoryMappedFile;
diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
index b9954bc..855a8c7 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
@@ -26,7 +26,6 @@
import java.security.Provider;
import java.util.ArrayList;
import java.util.Locale;
-import java.util.Objects;
/**
* This class implements common functionality for Provider supplied
@@ -70,7 +69,7 @@
*
* }</pre>
*/
-public class Engine {
+public final class Engine {
/**
* Access to package visible api in java.security
@@ -93,19 +92,15 @@
private static final class ServiceCacheEntry {
/** used to test for cache hit */
private final String algorithm;
- /** used to test for cache hit */
- private final Provider provider;
/** used to test for cache validity */
private final int cacheVersion;
/** cached result */
private final ArrayList<Provider.Service> services;
private ServiceCacheEntry(String algorithm,
- Provider provider,
int cacheVersion,
ArrayList<Provider.Service> services) {
this.algorithm = algorithm;
- this.provider = provider;
this.cacheVersion = cacheVersion;
this.services = services;
}
@@ -139,7 +134,7 @@
if (algorithm == null) {
throw new NoSuchAlgorithmException("Null algorithm name");
}
- ArrayList<Provider.Service> services = getServices(algorithm, null);
+ ArrayList<Provider.Service> services = getServices(algorithm);
if (services == null) {
throw notFound(this.serviceName, algorithm);
}
@@ -159,32 +154,19 @@
/**
* Returns a list of all possible matches for a given algorithm.
*/
- public ArrayList<Provider.Service> getServices(String algorithm, Provider provider) {
+ public ArrayList<Provider.Service> getServices(String algorithm) {
int newCacheVersion = Services.getCacheVersion();
ServiceCacheEntry cacheEntry = this.serviceCache;
final String algoUC = algorithm.toUpperCase(Locale.US);
if (cacheEntry != null
&& cacheEntry.algorithm.equalsIgnoreCase(algoUC)
- && Objects.equals(cacheEntry.provider, provider)
&& newCacheVersion == cacheEntry.cacheVersion) {
return cacheEntry.services;
}
String name = this.serviceName + "." + algoUC;
ArrayList<Provider.Service> services = Services.getServices(name);
- if (provider == null || services == null) {
- this.serviceCache = new ServiceCacheEntry(algoUC, provider, newCacheVersion, services);
- return services;
- }
- ArrayList<Provider.Service> filteredServices = new ArrayList<Provider.Service>(
- services.size());
- for (Provider.Service service : services) {
- if (provider.equals(service.getProvider())) {
- filteredServices.add(service);
- }
- }
- this.serviceCache = new ServiceCacheEntry(algoUC, provider, newCacheVersion,
- filteredServices);
- return filteredServices;
+ this.serviceCache = new ServiceCacheEntry(algoUC, newCacheVersion, services);
+ return services;
}
/**
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/Cache.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/Cache.java
deleted file mode 100644
index a2c5b4c..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/Cache.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-/**
-* @author Alexander Y. Kleymenov
-* @version $Revision$
-*/
-
-package org.apache.harmony.security.provider.cert;
-
-import java.util.Arrays;
-
-/**
- * The caching mechanism designed to speed up the process
- * of Certificates/CRLs generation in the case of their repeated
- * generation.
- *
- * It keeps correspondences between Objects (Certificates or CLRs)
- * and arrays of bytes on the base of which the Objects have been generated,
- * and provides the means to determine whether it contains the object built on
- * the base of particular encoded form or not. If there are such
- * objects they are returned from the cache, if not - newly generated
- * objects can be saved in the cache.<br>
- *
- * The process of Certificate/CRL generation
- * (implemented in <code>X509CertFactoryImpl</code>) is accompanied with
- * prereading of the beginning of encoded form. This prefix is used to determine
- * whether provided form is PEM encoding or not.<br>
- *
- * So the use of the prefix is the first point to (approximately)
- * determine whether object to be generated is in the cache or not.
- *
- * The failure of the predetermination process tells us that there were not
- * object generated from the encoded form with such prefix and we should
- * generate (decode) the object. If predetermination is successful,
- * we conduct the accurate search on the base of whole encoded form. <br>
- *
- * So to speed up the object generation process this caching mechanism provides
- * the following functionality:<br>
- *
- * 1. With having of the beginning of the encoded form (prefix)
- * it is possible to predetermine whether object has already been
- * generated on the base of the encoding with the SIMILAR prefix or not.
- * This process is not computationally expensive and takes a little time.
- * But it prevents us from use of expensive full encoding
- * search in the case of its failure.<br>
- *
- * 2. If predetermination ends with success, the whole encoding
- * form should be provided to make the final answer: whether object has
- * already been generated on the base of this PARTICULAR encoded form or not.
- * If it is so - the cached object is returned from the cache,
- * if not - new object should be generated and saved in the cache.<br>
- *
- * Note: The length of the prefixes of the encoded forms should not be
- * less than correspondence (default value is 28).
- */
-public class Cache {
-
- // Hash code consist of 6 bytes: AABB00
- // where:
- // AA - 2 bytes for prefix hash
- // value generated on the base of the prefix of encoding
- // BB - 2 bytes for tail hash
- // value generated on the base of the tail of encoding
- // 00 - 2 reserved bytes equals to 0
- //
- // Note, that it is possible for 2 different arrays to have
- // the similar hash codes.
-
- // The masks to work with hash codes:
- // the hash code without the reserved bytes
- private static final long HASH_MASK = 0xFFFFFFFFFFFF0000L;
- // the hash code of the prefix
- private static final long PREFIX_HASH_MASK = 0xFFFFFFFF00000000L;
- // the index value contained in reserved bytes
- private static final int INDEX_MASK = 0x00FFFF;
-
- // size of the cache
- private final int cache_size;
- // the number of bytes which will be used for array hash generation.
- private final int prefix_size;
-
- // The following 3 arrays contain the information about cached objects.
- // This information includes: hash of the array, encoded form of the object,
- // and the object itself.
- // The hash-encoding-object correspondence is made by means of index
- // in the particular array. I.e. for index N hash contained in hashes[N]
- // corresponds to the encoding contained in encodings[N] which corresponds
- // to the object cached at cache[N]
-
- // array containing the hash codes of encodings
- private final long[] hashes;
- // array containing the encodings of the cached objects
- private final byte[][] encodings;
- // array containing the cached objects
- private final Object[] cache;
-
- // This array is used to speed up the process of the search in the cache.
- // This is an ordered array of the hash codes from 'hashes' array (described
- // above) with last 2 (reserved) bytes equals to the index of
- // the hash in the 'hashes' array. I.e. hash code ABCD00 with index 10 in
- // the hashes array will be represented in this array as ABCD0A (10==0x0A)
- // So this array contains ordered <hash to index> correspondences.
- // Note, that every item in this array is unique.
- private final long[] hashes_idx;
-
- // the index of the last cached object
- private int last_cached = 0;
- // cache population indicator
- private boolean cache_is_full = false;
-
- /**
- * Creates the Cache object.
- * @param pref_size specifies how many leading/trailing bytes of object's
- * encoded form will be used for hash computation
- * @param size capacity of the cache to be created.
- */
- public Cache(int pref_size, int size) {
- cache_size = size;
- prefix_size = pref_size;
- hashes = new long[cache_size];
- hashes_idx = new long[cache_size];
- encodings = new byte[cache_size][];
- cache = new Object[cache_size];
- }
-
- /**
- * Creates the Cache object of size of 9.
- * @param pref_size specifies how many leading/trailing bytes of object's
- * encoded form will be used for hash computation
- */
- public Cache(int pref_size) {
- this(pref_size, 9);
- }
-
- /**
- * Creates the Cache object of size of 9.
- */
- public Cache() {
- this(28, 9);
- }
-
- /**
- * Returns the hash code for the array. This code is used to
- * predetermine whether the object was built on the base of the
- * similar encoding or not (by means of <code>contains(long)</code> method),
- * to exactly determine whether object is contained in the cache or not,
- * and to put the object in the cache.
- * Note: parameter array should be of length not less than
- * specified by <code>prefix_size</code> (default 28)
- * @param arr the byte array containing at least prefix_size leading bytes
- * of the encoding.
- * @return hash code for specified encoding prefix
- */
- public long getHash(byte[] arr) {
- long hash = 0;
- for (int i=1; i<prefix_size; i++) {
- hash += (arr[i] & 0xFF);
- } // it takes about 2 bytes for prefix_size == 28
-
- // shift to the correct place
- hash = hash << 32;
- return hash;
- }
-
- /**
- * Checks if there are any object in the cache generated
- * on the base of encoding with prefix corresponding
- * to the specified hash code.
- * @param prefix_hash the hash code for the prefix
- * of the encoding (retrieved by method <code>getHash(byte[]))</code>
- * @return false if there were not any object generated
- * on the base of encoding with specified hash code, true
- * otherwise.
- */
- public boolean contains(long prefix_hash) {
- if (prefix_hash == 0) {
- return false;
- }
- int idx = -1*Arrays.binarySearch(hashes_idx, prefix_hash)-1;
- if (idx == cache_size) {
- return false;
- } else {
- return (hashes_idx[idx] & PREFIX_HASH_MASK) == prefix_hash;
- }
- }
-
- /**
- * Returns the object built on the base on the specified encoded
- * form if it is contained in the cache and null otherwise.
- * This method is computationally expensive and should be called only if
- * the method <code>contains(long)</code> for the hash code returned true.
- * @param hash the hash code for the prefix of the encoding
- * (retrieved by method <code>getHash(byte[])</code>)
- * @param encoding encoded form of the required object.
- * @return the object corresponding to specified encoding or null if
- * there is no such correspondence.
- */
- public Object get(long hash, byte[] encoding) {
- hash |= getSuffHash(encoding);
- if (hash == 0) {
- return null;
- }
- int idx = -1*Arrays.binarySearch(hashes_idx, hash)-1;
- if (idx == cache_size) {
- return null;
- }
- while ((hashes_idx[idx] & HASH_MASK) == hash) {
- int i = (int) (hashes_idx[idx] & INDEX_MASK) - 1;
- if (Arrays.equals(encoding, encodings[i])) {
- return cache[i];
- }
- idx++;
- if (idx == cache_size) {
- return null;
- }
- }
- return null;
- }
-
- /**
- * Puts the object into the cache.
- * @param hash hash code for the prefix of the encoding
- * @param encoding the encoded form of the object
- * @param object the object to be saved in the cache
- */
- public void put(long hash, byte[] encoding, Object object) {
- // check for empty space in the cache
- if (last_cached == cache_size) {
- // so cache is full, will erase the first entry in the
- // cache (oldest entry). it could be better to throw out
- // rarely used value instead of oldest one..
- last_cached = 0;
- cache_is_full = true;
- }
- // index pointing to the item of the table to be overwritten
- int index = last_cached++;
-
- // improve the hash value with info from the tail of encoding
- hash |= getSuffHash(encoding);
-
- if (cache_is_full) {
- // indexing hash value to be overwritten:
- long idx_hash = (hashes[index] | (index+1));
- int idx = Arrays.binarySearch(hashes_idx, idx_hash);
- if (idx < 0) {
- // it will never happen because we use saved hash value
- // (hashes[index])
- System.out.println("WARNING! "+idx);
- idx = -(idx + 1);
- }
- long new_hash_idx = (hash | (index + 1));
- int new_idx = Arrays.binarySearch(hashes_idx, new_hash_idx);
- if (new_idx >= 0) {
- // it's possible when we write the same hash in the same cell
- if (idx != new_idx) {
- // it will never happen because we use the same
- // hash and the same index in hash table
- System.out.println("WARNING: ");
- System.out.println(">> idx: "+idx+" new_idx: "+new_idx);
- }
- } else {
- new_idx = -(new_idx + 1);
- // replace in sorted array
- if (new_idx > idx) {
- System.arraycopy(hashes_idx, idx+1, hashes_idx, idx,
- new_idx - idx - 1);
- hashes_idx[new_idx-1] = new_hash_idx;
- } else if (idx > new_idx) {
- System.arraycopy(hashes_idx, new_idx, hashes_idx, new_idx+1,
- idx - new_idx);
- hashes_idx[new_idx] = new_hash_idx;
- } else { // idx == new_idx
- hashes_idx[new_idx] = new_hash_idx;
- }
- }
- } else {
- long idx_hash = (hash | (index + 1));
- int idx = Arrays.binarySearch(hashes_idx, idx_hash);
- if (idx < 0) {
- // it will always be true because idx_hash depends on index
- idx = -(idx + 1);
- }
- idx = idx - 1;
- if (idx != cache_size - index - 1) {
- // if not in the cell containing 0 (free cell), do copy:
- System.arraycopy(hashes_idx, cache_size - index,
- hashes_idx, cache_size - index - 1,
- idx - (cache_size - index) + 1);
- }
- hashes_idx[idx] = idx_hash;
- }
- // overwrite the values in the tables:
- hashes[index] = hash;
- encodings[index] = encoding;
- cache[index] = object;
- }
-
- // Returns the hash code built on the base of the tail of the encoded form
- // @param arr - the array containing at least prefix_size trailing bytes
- // of encoded form
- private long getSuffHash(byte[] arr) {
- long hash_addon = 0;
- for (int i=arr.length-1; i>arr.length - prefix_size; i--) {
- hash_addon += (arr[i] & 0xFF);
- }
- return hash_addon << 16;
- }
-
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/DRLCertFactory.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/DRLCertFactory.java
deleted file mode 100644
index 790be67..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/DRLCertFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-/**
-* @author Alexander Y. Kleymenov
-* @version $Revision$
-*/
-
-package org.apache.harmony.security.provider.cert;
-
-import java.security.Provider;
-
-public final class DRLCertFactory extends Provider {
- /**
- * @serial
- */
- private static final long serialVersionUID = -7269650779605195879L;
-
- /**
- * Constructs the instance of the certificate factory provider.
- */
- public DRLCertFactory() {
- // specification of the provider name, version, and description.
- super("DRLCertFactory", 1.0, "ASN.1, DER, PkiPath, PKCS7");
- // register the service
- put("CertificateFactory.X509", "org.apache.harmony.security.provider.cert.X509CertFactoryImpl");
- // mapping the alias
- put("Alg.Alias.CertificateFactory.X.509", "X509");
- }
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLEntryImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLEntryImpl.java
deleted file mode 100644
index 38500e5..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLEntryImpl.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-/**
-* @author Alexander Y. Kleymenov
-* @version $Revision$
-*/
-
-package org.apache.harmony.security.provider.cert;
-
-import java.math.BigInteger;
-import java.security.cert.CRLException;
-import java.security.cert.X509CRLEntry;
-import java.util.Date;
-import java.util.Set;
-import javax.security.auth.x500.X500Principal;
-import org.apache.harmony.security.x509.Extension;
-import org.apache.harmony.security.x509.Extensions;
-import org.apache.harmony.security.x509.TBSCertList;
-
-/**
- * Implementation of X509CRLEntry. It wraps the instance
- * of org.apache.harmony.security.x509.TBSCertList.RevokedCertificate
- * obtained during the decoding of TBSCertList substructure
- * of the CertificateList structure which is an X.509 form of CRL.
- * (see RFC 3280 at http://www.ietf.org/rfc/rfc3280.txt)
- * Normally the instances of this class are constructed by involving
- * X509CRLImpl object.
- * @see org.apache.harmony.security.x509.TBSCertList
- * @see org.apache.harmony.security.provider.cert.X509CRLImpl
- * @see java.security.cert.X509CRLEntry
- */
-public class X509CRLEntryImpl extends X509CRLEntry {
-
- // the crl entry object to be wrapped in X509CRLEntry
- private final TBSCertList.RevokedCertificate rcert;
- // the extensions of the entry
- private final Extensions extensions;
- // issuer of the revoked certificate described by this crl entry
- private final X500Principal issuer;
-
- // encoded form of this revoked certificate entry
- private byte[] encoding;
-
- /**
- * Creates an instance on the base of existing
- * <code>TBSCertList.RevokedCertificate</code> object and
- * information about the issuer of revoked certificate.
- * If specified issuer is null, it is supposed that issuer
- * of the revoked certificate is the same as for involving CRL.
- */
- public X509CRLEntryImpl(TBSCertList.RevokedCertificate rcert,
- X500Principal issuer) {
- this.rcert = rcert;
- this.extensions = rcert.getCrlEntryExtensions();
- this.issuer = issuer;
- }
-
- // ---------------------------------------------------------------------
- // ------ java.security.cert.X509CRLEntry method implementations -------
- // ---------------------------------------------------------------------
-
- /**
- * @see java.security.cert.X509CRLEntry#getEncoded()
- * method documentation for more info
- */
- public byte[] getEncoded() throws CRLException {
- if (encoding == null) {
- encoding = rcert.getEncoded();
- }
- byte[] result = new byte[encoding.length];
- System.arraycopy(encoding, 0, result, 0, encoding.length);
- return result;
- }
-
- /**
- * @see java.security.cert.X509CRLEntry#getSerialNumber()
- * method documentation for more info
- */
- public BigInteger getSerialNumber() {
- return rcert.getUserCertificate();
- }
-
- /**
- * @see java.security.cert.X509CRLEntry#getCertificateIssuer()
- * method documentation for more info
- */
- public X500Principal getCertificateIssuer() {
- return issuer;
- }
-
- /**
- * @see java.security.cert.X509CRLEntry#getRevocationDate()
- * method documentation for more info
- */
- public Date getRevocationDate() {
- return rcert.getRevocationDate();
- }
-
- /**
- * @see java.security.cert.X509CRLEntry#hasExtensions()
- * method documentation for more info
- */
- public boolean hasExtensions() {
- return (extensions != null) && (extensions.size() != 0);
- }
-
- /**
- * @see java.security.cert.X509CRLEntry#toString()
- * method documentation for more info
- */
- public String toString() {
- return "X509CRLEntryImpl: "+rcert.toString();
- }
-
- // ---------------------------------------------------------------------
- // ------ java.security.cert.X509Extension method implementations ------
- // ---------------------------------------------------------------------
-
- /**
- * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs()
- * method documentation for more info
- */
- public Set getNonCriticalExtensionOIDs() {
- if (extensions == null) {
- return null;
- }
- return extensions.getNonCriticalExtensions();
- }
-
- /**
- * @see java.security.cert.X509Extension#getCriticalExtensionOIDs()
- * method documentation for more info
- */
- public Set getCriticalExtensionOIDs() {
- if (extensions == null) {
- return null;
- }
- return extensions.getCriticalExtensions();
- }
-
- /**
- * @see java.security.cert.X509Extension#getExtensionValue(String)
- * method documentation for more info
- */
- public byte[] getExtensionValue(String oid) {
- if (extensions == null) {
- return null;
- }
- Extension ext = extensions.getExtensionByOID(oid);
- return (ext == null) ? null : ext.getRawExtnValue();
- }
-
- /**
- * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension()
- * method documentation for more info
- */
- public boolean hasUnsupportedCriticalExtension() {
- if (extensions == null) {
- return false;
- }
- return extensions.hasUnsupportedCritical();
- }
-}
-
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java
deleted file mode 100644
index de5bbfa..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-/**
-* @author Alexander Y. Kleymenov
-* @version $Revision$
-*/
-
-package org.apache.harmony.security.provider.cert;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Principal;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CRLException;
-import java.security.cert.Certificate;
-import java.security.cert.X509CRL;
-import java.security.cert.X509CRLEntry;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import javax.security.auth.x500.X500Principal;
-import org.apache.harmony.security.utils.AlgNameMapper;
-import org.apache.harmony.security.x509.CertificateList;
-import org.apache.harmony.security.x509.Extension;
-import org.apache.harmony.security.x509.Extensions;
-import org.apache.harmony.security.x509.TBSCertList;
-
-/**
- * This class is an implementation of X509CRL. It wraps
- * the instance of org.apache.harmony.security.x509.CertificateList
- * built on the base of provided ASN.1 DER encoded form of
- * CertificateList structure (as specified in RFC 3280
- * http://www.ietf.org/rfc/rfc3280.txt).
- * Implementation supports work with indirect CRLs.
- * @see org.apache.harmony.security.x509.CertificateList
- * @see java.security.cert.X509CRL
- */
-public class X509CRLImpl extends X509CRL {
-
- // the core object to be wrapped in X509CRL
- private final CertificateList crl;
-
- // To speed up access to the info, the following fields
- // cache values retrieved from the CertificateList object
- private final TBSCertList tbsCertList;
- private byte[] tbsCertListEncoding;
- private final Extensions extensions;
- private X500Principal issuer;
- private ArrayList entries;
- private int entriesSize;
- private byte[] signature;
- private String sigAlgOID;
- private String sigAlgName;
- private byte[] sigAlgParams;
-
- // encoded form of crl
- private byte[] encoding;
-
- // indicates whether the signature algorithm parameters are null
- private boolean nullSigAlgParams;
- // indicates whether the crl entries have already been retrieved
- // from CertificateList object (crl)
- private boolean entriesRetrieved;
-
- // indicates whether this X.509 CRL is direct or indirect
- // (see rfc 3280 http://www.ietf.org/rfc/rfc3280.txt, p 5.)
- private boolean isIndirectCRL;
- // if crl is indirect, this field holds an info about how
- // many of the leading certificates in the list are issued
- // by the same issuer as CRL.
- private int nonIndirectEntriesSize;
-
- /**
- * Creates X.509 CRL by wrapping of the specified CertificateList object.
- */
- public X509CRLImpl(CertificateList crl) {
- this.crl = crl;
- this.tbsCertList = crl.getTbsCertList();
- this.extensions = tbsCertList.getCrlExtensions();
- }
-
- /**
- * Creates X.509 CRL on the base of ASN.1 DER encoded form of
- * the CRL (CertificateList structure described in RFC 3280)
- * provided via input stream.
- * @throws CRLException if decoding errors occur.
- */
- public X509CRLImpl(InputStream in) throws CRLException {
- try {
- // decode CertificateList structure
- this.crl = (CertificateList) CertificateList.ASN1.decode(in);
- this.tbsCertList = crl.getTbsCertList();
- this.extensions = tbsCertList.getCrlExtensions();
- } catch (IOException e) {
- throw new CRLException(e);
- }
- }
-
- /**
- * Creates X.509 CRL on the base of ASN.1 DER encoded form of
- * the CRL (CertificateList structure described in RFC 3280)
- * provided via array of bytes.
- * @throws IOException if decoding errors occur.
- */
- public X509CRLImpl(byte[] encoding) throws IOException {
- this((CertificateList) CertificateList.ASN1.decode(encoding));
- }
-
- // ---------------------------------------------------------------------
- // ----- java.security.cert.X509CRL abstract method implementations ----
- // ---------------------------------------------------------------------
-
- /**
- * @see java.security.cert.X509CRL#getEncoded()
- * method documentation for more info
- */
- public byte[] getEncoded() throws CRLException {
- if (encoding == null) {
- encoding = crl.getEncoded();
- }
- byte[] result = new byte[encoding.length];
- System.arraycopy(encoding, 0, result, 0, encoding.length);
- return result;
- }
-
- /**
- * @see java.security.cert.X509CRL#getVersion()
- * method documentation for more info
- */
- public int getVersion() {
- return tbsCertList.getVersion();
- }
-
- /**
- * @see java.security.cert.X509CRL#getIssuerDN()
- * method documentation for more info
- */
- public Principal getIssuerDN() {
- if (issuer == null) {
- issuer = tbsCertList.getIssuer().getX500Principal();
- }
- return issuer;
- }
-
- /**
- * @see java.security.cert.X509CRL#getIssuerX500Principal()
- * method documentation for more info
- */
- public X500Principal getIssuerX500Principal() {
- if (issuer == null) {
- issuer = tbsCertList.getIssuer().getX500Principal();
- }
- return issuer;
- }
-
- /**
- * @see java.security.cert.X509CRL#getThisUpdate()
- * method documentation for more info
- */
- public Date getThisUpdate() {
- return tbsCertList.getThisUpdate();
- }
-
- /**
- * @see java.security.cert.X509CRL#getNextUpdate()
- * method documentation for more info
- */
- public Date getNextUpdate() {
- return tbsCertList.getNextUpdate();
- }
-
- /*
- * Retrieves the crl entries (TBSCertList.RevokedCertificate objects)
- * from the TBSCertList structure and converts them to the
- * X509CRLEntryImpl objects
- */
- private void retrieveEntries() {
- entriesRetrieved = true;
- List rcerts = tbsCertList.getRevokedCertificates();
- if (rcerts == null) {
- return;
- }
- entriesSize = rcerts.size();
- entries = new ArrayList(entriesSize);
- // null means that revoked certificate issuer is the same as CRL issuer
- X500Principal rcertIssuer = null;
- for (int i=0; i<entriesSize; i++) {
- TBSCertList.RevokedCertificate rcert =
- (TBSCertList.RevokedCertificate) rcerts.get(i);
- X500Principal iss = rcert.getIssuer();
- if (iss != null) {
- // certificate issuer differs from CRL issuer
- // and CRL is indirect.
- rcertIssuer = iss;
- isIndirectCRL = true;
- // remember how many leading revoked certificates in the
- // list are issued by the same issuer as issuer of CRL
- // (these certificates are first in the list)
- nonIndirectEntriesSize = i;
- }
- entries.add(new X509CRLEntryImpl(rcert, rcertIssuer));
- }
- }
-
- /**
- * Searches for certificate in CRL.
- * This method supports indirect CRLs: if CRL is indirect method takes
- * into account serial number and issuer of the certificate,
- * if CRL issued by CA (i.e. it is not indirect) search is done only
- * by serial number of the specified certificate.
- * @see java.security.cert.X509CRL#getRevokedCertificate(X509Certificate)
- * method documentation for more info
- */
- public X509CRLEntry getRevokedCertificate(X509Certificate certificate) {
- if (certificate == null) {
- throw new NullPointerException("certificate == null");
- }
- if (!entriesRetrieved) {
- retrieveEntries();
- }
- if (entries == null) {
- return null;
- }
- BigInteger serialN = certificate.getSerialNumber();
- if (isIndirectCRL) {
- // search in indirect crl
- X500Principal certIssuer = certificate.getIssuerX500Principal();
- if (certIssuer.equals(getIssuerX500Principal())) {
- // certificate issuer is CRL issuer
- certIssuer = null;
- }
- for (int i=0; i<entriesSize; i++) {
- X509CRLEntry entry = (X509CRLEntry) entries.get(i);
- // check the serial number of revoked certificate
- if (serialN.equals(entry.getSerialNumber())) {
- // revoked certificate issuer
- X500Principal iss = entry.getCertificateIssuer();
- // check the issuer of revoked certificate
- if (certIssuer != null) {
- // certificate issuer is not a CRL issuer, so
- // check issuers for equality
- if (certIssuer.equals(iss)) {
- return entry;
- }
- } else if (iss == null) {
- // both certificates was issued by CRL issuer
- return entry;
- }
- }
- }
- } else {
- // search in CA's (non indirect) crl: just look up the serial number
- for (int i=0; i<entriesSize; i++) {
- X509CRLEntry entry = (X509CRLEntry) entries.get(i);
- if (serialN.equals(entry.getSerialNumber())) {
- return entry;
- }
- }
- }
- return null;
- }
-
- /**
- * Method searches for CRL entry with specified serial number.
- * The method will search only certificate issued by CRL's issuer.
- * @see java.security.cert.X509CRL#getRevokedCertificate(BigInteger)
- * method documentation for more info
- */
- public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) {
- if (!entriesRetrieved) {
- retrieveEntries();
- }
- if (entries == null) {
- return null;
- }
- if (isIndirectCRL) {
- for (int i = 0; i < nonIndirectEntriesSize; i++) {
- X509CRLEntry entry = (X509CRLEntry) entries.get(i);
- if (serialNumber.equals(entry.getSerialNumber())
- && entry.getCertificateIssuer() == null) {
- return entry;
- }
- }
- } else {
- for (int i = 0; i < entriesSize; i++) {
- X509CRLEntry entry = (X509CRLEntry) entries.get(i);
- if (serialNumber.equals(entry.getSerialNumber())) {
- return entry;
- }
- }
- }
- return null;
- }
-
- /**
- * @see java.security.cert.X509CRL#getRevokedCertificates()
- * method documentation for more info
- */
- public Set<? extends X509CRLEntry> getRevokedCertificates() {
- if (!entriesRetrieved) {
- retrieveEntries();
- }
- if (entries == null) {
- return null;
- }
- return new HashSet(entries);
- }
-
- /**
- * @see java.security.cert.X509CRL#getTBSCertList()
- * method documentation for more info
- */
- public byte[] getTBSCertList() throws CRLException {
- if (tbsCertListEncoding == null) {
- tbsCertListEncoding = tbsCertList.getEncoded();
- }
- byte[] result = new byte[tbsCertListEncoding.length];
- System.arraycopy(tbsCertListEncoding, 0,
- result, 0, tbsCertListEncoding.length);
- return result;
- }
-
- /**
- * @see java.security.cert.X509CRL#getSignature()
- * method documentation for more info
- */
- public byte[] getSignature() {
- if (signature == null) {
- signature = crl.getSignatureValue();
- }
- byte[] result = new byte[signature.length];
- System.arraycopy(signature, 0, result, 0, signature.length);
- return result;
- }
-
- /**
- * @see java.security.cert.X509CRL#getSigAlgName()
- * method documentation for more info
- */
- public String getSigAlgName() {
- if (sigAlgOID == null) {
- sigAlgOID = tbsCertList.getSignature().getAlgorithm();
- sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID);
- if (sigAlgName == null) {
- sigAlgName = sigAlgOID;
- }
- }
- return sigAlgName;
- }
-
- /**
- * @see java.security.cert.X509CRL#getSigAlgOID()
- * method documentation for more info
- */
- public String getSigAlgOID() {
- if (sigAlgOID == null) {
- sigAlgOID = tbsCertList.getSignature().getAlgorithm();
- sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID);
- if (sigAlgName == null) {
- sigAlgName = sigAlgOID;
- }
- }
- return sigAlgOID;
- }
-
- /**
- * @see java.security.cert.X509CRL#getSigAlgParams()
- * method documentation for more info
- */
- public byte[] getSigAlgParams() {
- if (nullSigAlgParams) {
- return null;
- }
- if (sigAlgParams == null) {
- sigAlgParams = tbsCertList.getSignature().getParameters();
- if (sigAlgParams == null) {
- nullSigAlgParams = true;
- return null;
- }
- }
- return sigAlgParams;
- }
-
- /**
- * @see java.security.cert.X509CRL#verify(PublicKey key)
- * method documentation for more info
- */
- public void verify(PublicKey key)
- throws CRLException, NoSuchAlgorithmException,
- InvalidKeyException, NoSuchProviderException,
- SignatureException {
- Signature signature = Signature.getInstance(getSigAlgName());
- signature.initVerify(key);
- byte[] tbsEncoding = tbsCertList.getEncoded();
- signature.update(tbsEncoding, 0, tbsEncoding.length);
- if (!signature.verify(crl.getSignatureValue())) {
- throw new SignatureException("Signature was not verified");
- }
- }
-
- /**
- * @see java.security.cert.X509CRL#verify(PublicKey key, String sigProvider)
- * method documentation for more info
- */
- public void verify(PublicKey key, String sigProvider)
- throws CRLException, NoSuchAlgorithmException,
- InvalidKeyException, NoSuchProviderException,
- SignatureException {
- Signature signature = Signature.getInstance(
- getSigAlgName(), sigProvider);
- signature.initVerify(key);
- byte[] tbsEncoding = tbsCertList.getEncoded();
- signature.update(tbsEncoding, 0, tbsEncoding.length);
- if (!signature.verify(crl.getSignatureValue())) {
- throw new SignatureException("Signature was not verified");
- }
- }
-
- // ---------------------------------------------------------------------
- // ------ java.security.cert.CRL abstract method implementations -------
- // ---------------------------------------------------------------------
-
- /**
- * @see java.security.cert.CRL#isRevoked(Certificate)
- * method documentation for more info
- */
- public boolean isRevoked(Certificate cert) {
- if (!(cert instanceof X509Certificate)) {
- return false;
- }
- return getRevokedCertificate((X509Certificate) cert) != null;
- }
-
- /**
- * @see java.security.cert.CRL#toString()
- * method documentation for more info
- */
- public String toString() {
- return crl.toString();
- }
-
- // ---------------------------------------------------------------------
- // ------ java.security.cert.X509Extension method implementations ------
- // ---------------------------------------------------------------------
-
- /**
- * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs()
- * method documentation for more info
- */
- public Set getNonCriticalExtensionOIDs() {
- if (extensions == null) {
- return null;
- }
- return extensions.getNonCriticalExtensions();
- }
-
- /**
- * @see java.security.cert.X509Extension#getCriticalExtensionOIDs()
- * method documentation for more info
- */
- public Set getCriticalExtensionOIDs() {
- if (extensions == null) {
- return null;
- }
- return extensions.getCriticalExtensions();
- }
-
- /**
- * @see java.security.cert.X509Extension#getExtensionValue(String)
- * method documentation for more info
- */
- public byte[] getExtensionValue(String oid) {
- if (extensions == null) {
- return null;
- }
- Extension ext = extensions.getExtensionByOID(oid);
- return (ext == null) ? null : ext.getRawExtnValue();
- }
-
- /**
- * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension()
- * method documentation for more info
- */
- public boolean hasUnsupportedCriticalExtension() {
- if (extensions == null) {
- return false;
- }
- return extensions.hasUnsupportedCritical();
- }
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java
deleted file mode 100644
index 9129ec2..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java
+++ /dev/null
@@ -1,858 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-/**
-* @author Alexander Y. Kleymenov
-* @version $Revision$
-*/
-
-package org.apache.harmony.security.provider.cert;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.security.cert.CRL;
-import java.security.cert.CRLException;
-import java.security.cert.CertPath;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactorySpi;
-import java.security.cert.X509CRL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import libcore.io.Base64;
-import libcore.io.Streams;
-import org.apache.harmony.security.asn1.ASN1Constants;
-import org.apache.harmony.security.asn1.BerInputStream;
-import org.apache.harmony.security.pkcs7.ContentInfo;
-import org.apache.harmony.security.pkcs7.SignedData;
-import org.apache.harmony.security.x509.CertificateList;
-
-/**
- * X509 Certificate Factory Service Provider Interface Implementation.
- * It supports CRLs and Certificates in (PEM) ASN.1 DER encoded form,
- * and Certification Paths in PkiPath and PKCS7 formats.
- * For Certificates and CRLs factory maintains the caching
- * mechanisms allowing to speed up repeated Certificate/CRL
- * generation.
- * @see Cache
- */
-public class X509CertFactoryImpl extends CertificateFactorySpi {
-
- // number of leading/trailing bytes used for cert hash computation
- private static final int CERT_CACHE_SEED_LENGTH = 28;
- // certificate cache
- private static final Cache CERT_CACHE = new Cache(CERT_CACHE_SEED_LENGTH);
- // number of leading/trailing bytes used for crl hash computation
- private static final int CRL_CACHE_SEED_LENGTH = 24;
- // crl cache
- private static final Cache CRL_CACHE = new Cache(CRL_CACHE_SEED_LENGTH);
-
- /**
- * Default constructor.
- * Creates the instance of Certificate Factory SPI ready for use.
- */
- public X509CertFactoryImpl() { }
-
- /**
- * Generates the X.509 certificate from the data in the stream.
- * The data in the stream can be either in ASN.1 DER encoded X.509
- * certificate, or PEM (Base64 encoding bounded by
- * <code>"-----BEGIN CERTIFICATE-----"</code> at the beginning and
- * <code>"-----END CERTIFICATE-----"</code> at the end) representation
- * of the former encoded form.
- *
- * Before the generation the encoded form is looked up in
- * the cache. If the cache contains the certificate with requested encoded
- * form it is returned from it, otherwise it is generated by ASN.1
- * decoder.
- *
- * @see java.security.cert.CertificateFactorySpi#engineGenerateCertificate(InputStream)
- * method documentation for more info
- */
- public Certificate engineGenerateCertificate(InputStream inStream)
- throws CertificateException {
- if (inStream == null) {
- throw new CertificateException("inStream == null");
- }
- try {
- if (!inStream.markSupported()) {
- // create the mark supporting wrapper
- inStream = new RestoringInputStream(inStream);
- }
- // mark is needed to recognize the format of the provided encoding
- // (ASN.1 or PEM)
- inStream.mark(1);
- // check whether the provided certificate is in PEM encoded form
- if (inStream.read() == '-') {
- // decode PEM, retrieve CRL
- return getCertificate(decodePEM(inStream, CERT_BOUND_SUFFIX));
- } else {
- inStream.reset();
- // retrieve CRL
- return getCertificate(inStream);
- }
- } catch (IOException e) {
- throw new CertificateException(e);
- }
- }
-
- /**
- * Generates the collection of the certificates on the base of provided
- * via input stream encodings.
- * @see java.security.cert.CertificateFactorySpi#engineGenerateCertificates(InputStream)
- * method documentation for more info
- */
- public Collection<? extends Certificate>
- engineGenerateCertificates(InputStream inStream)
- throws CertificateException {
- if (inStream == null) {
- throw new CertificateException("inStream == null");
- }
- ArrayList<Certificate> result = new ArrayList<Certificate>();
- try {
- if (!inStream.markSupported()) {
- // create the mark supporting wrapper
- inStream = new RestoringInputStream(inStream);
- }
- // if it is PEM encoded form this array will contain the encoding
- // so ((it is PEM) <-> (encoding != null))
- byte[] encoding = null;
- // The following by SEQUENCE ASN.1 tag, used for
- // recognizing the data format
- // (is it PKCS7 ContentInfo structure, X.509 Certificate, or
- // unsupported encoding)
- int second_asn1_tag = -1;
- inStream.mark(1);
- int ch;
- while ((ch = inStream.read()) != -1) {
- // check if it is PEM encoded form
- if (ch == '-') { // beginning of PEM encoding ('-' char)
- // decode PEM chunk and store its content (ASN.1 encoding)
- encoding = decodePEM(inStream, FREE_BOUND_SUFFIX);
- } else if (ch == 0x30) { // beginning of ASN.1 sequence (0x30)
- encoding = null;
- inStream.reset();
- // prepare for data format determination
- inStream.mark(CERT_CACHE_SEED_LENGTH);
- } else { // unsupported data
- if (result.size() == 0) {
- throw new CertificateException("Unsupported encoding");
- } else {
- // it can be trailing user data,
- // so keep it in the stream
- inStream.reset();
- return result;
- }
- }
- // Check the data format
- BerInputStream in = (encoding == null)
- ? new BerInputStream(inStream)
- : new BerInputStream(encoding);
- // read the next ASN.1 tag
- second_asn1_tag = in.next(); // inStream position changed
- if (encoding == null) {
- // keep whole structure in the stream
- inStream.reset();
- }
- // check if it is a TBSCertificate structure
- if (second_asn1_tag != ASN1Constants.TAG_C_SEQUENCE) {
- if (result.size() == 0) {
- // there were not read X.509 Certificates, so
- // break the cycle and check
- // whether it is PKCS7 structure
- break;
- } else {
- // it can be trailing user data,
- // so return what we already read
- return result;
- }
- } else {
- if (encoding == null) {
- result.add(getCertificate(inStream));
- } else {
- result.add(getCertificate(encoding));
- }
- }
- // mark for the next iteration
- inStream.mark(1);
- }
- if (result.size() != 0) {
- // some Certificates have been read
- return result;
- } else if (ch == -1) {
- /* No data in the stream, so return the empty collection. */
- return result;
- }
- // else: check if it is PKCS7
- if (second_asn1_tag == ASN1Constants.TAG_OID) {
- // it is PKCS7 ContentInfo structure, so decode it
- ContentInfo info = (ContentInfo)
- ((encoding != null)
- ? ContentInfo.ASN1.decode(encoding)
- : ContentInfo.ASN1.decode(inStream));
- // retrieve SignedData
- SignedData data = info.getSignedData();
- if (data == null) {
- throw new CertificateException("Invalid PKCS7 data provided");
- }
- List<org.apache.harmony.security.x509.Certificate> certs = data.getCertificates();
- if (certs != null) {
- for (org.apache.harmony.security.x509.Certificate cert : certs) {
- result.add(new X509CertImpl(cert));
- }
- }
- return result;
- }
- // else: Unknown data format
- throw new CertificateException("Unsupported encoding");
- } catch (IOException e) {
- throw new CertificateException(e);
- }
- }
-
- /**
- * @see java.security.cert.CertificateFactorySpi#engineGenerateCRL(InputStream)
- * method documentation for more info
- */
- public CRL engineGenerateCRL(InputStream inStream)
- throws CRLException {
- if (inStream == null) {
- throw new CRLException("inStream == null");
- }
- try {
- if (!inStream.markSupported()) {
- // Create the mark supporting wrapper
- // Mark is needed to recognize the format
- // of provided encoding form (ASN.1 or PEM)
- inStream = new RestoringInputStream(inStream);
- }
- inStream.mark(1);
- // check whether the provided crl is in PEM encoded form
- if (inStream.read() == '-') {
- // decode PEM, retrieve CRL
- return getCRL(decodePEM(inStream, FREE_BOUND_SUFFIX));
- } else {
- inStream.reset();
- // retrieve CRL
- return getCRL(inStream);
- }
- } catch (IOException e) {
- throw new CRLException(e);
- }
- }
-
- /**
- * @see java.security.cert.CertificateFactorySpi#engineGenerateCRLs(InputStream)
- * method documentation for more info
- */
- public Collection<? extends CRL> engineGenerateCRLs(InputStream inStream)
- throws CRLException {
- if (inStream == null) {
- throw new CRLException("inStream == null");
- }
- ArrayList<CRL> result = new ArrayList<CRL>();
- try {
- if (!inStream.markSupported()) {
- inStream = new RestoringInputStream(inStream);
- }
- // if it is PEM encoded form this array will contain the encoding
- // so ((it is PEM) <-> (encoding != null))
- byte[] encoding = null;
- // The following by SEQUENCE ASN.1 tag, used for
- // recognizing the data format
- // (is it PKCS7 ContentInfo structure, X.509 CRL, or
- // unsupported encoding)
- int second_asn1_tag = -1;
- inStream.mark(1);
- int ch;
- while ((ch = inStream.read()) != -1) {
- // check if it is PEM encoded form
- if (ch == '-') { // beginning of PEM encoding ('-' char)
- // decode PEM chunk and store its content (ASN.1 encoding)
- encoding = decodePEM(inStream, FREE_BOUND_SUFFIX);
- } else if (ch == 0x30) { // beginning of ASN.1 sequence (0x30)
- encoding = null;
- inStream.reset();
- // prepare for data format determination
- inStream.mark(CRL_CACHE_SEED_LENGTH);
- } else { // unsupported data
- if (result.size() == 0) {
- throw new CRLException("Unsupported encoding");
- } else {
- // it can be trailing user data,
- // so keep it in the stream
- inStream.reset();
- return result;
- }
- }
- // Check the data format
- BerInputStream in = (encoding == null)
- ? new BerInputStream(inStream)
- : new BerInputStream(encoding);
- // read the next ASN.1 tag
- second_asn1_tag = in.next();
- if (encoding == null) {
- // keep whole structure in the stream
- inStream.reset();
- }
- // check if it is a TBSCertList structure
- if (second_asn1_tag != ASN1Constants.TAG_C_SEQUENCE) {
- if (result.size() == 0) {
- // there were not read X.509 CRLs, so
- // break the cycle and check
- // whether it is PKCS7 structure
- break;
- } else {
- // it can be trailing user data,
- // so return what we already read
- return result;
- }
- } else {
- if (encoding == null) {
- result.add(getCRL(inStream));
- } else {
- result.add(getCRL(encoding));
- }
- }
- inStream.mark(1);
- }
- if (result.size() != 0) {
- // the stream was read out
- return result;
- } else if (ch == -1) {
- throw new CRLException("There is no data in the stream");
- }
- // else: check if it is PKCS7
- if (second_asn1_tag == ASN1Constants.TAG_OID) {
- // it is PKCS7 ContentInfo structure, so decode it
- ContentInfo info = (ContentInfo)
- ((encoding != null)
- ? ContentInfo.ASN1.decode(encoding)
- : ContentInfo.ASN1.decode(inStream));
- // retrieve SignedData
- SignedData data = info.getSignedData();
- if (data == null) {
- throw new CRLException("Invalid PKCS7 data provided");
- }
- List<CertificateList> crls = data.getCRLs();
- if (crls != null) {
- for (CertificateList crl : crls) {
- result.add(new X509CRLImpl(crl));
- }
- }
- return result;
- }
- // else: Unknown data format
- throw new CRLException("Unsupported encoding");
- } catch (IOException e) {
- throw new CRLException(e);
- }
- }
-
- /**
- * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(InputStream)
- * method documentation for more info
- */
- public CertPath engineGenerateCertPath(InputStream inStream)
- throws CertificateException {
- if (inStream == null) {
- throw new CertificateException("inStream == null");
- }
- return engineGenerateCertPath(inStream, "PkiPath");
- }
-
- /**
- * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(InputStream,String)
- * method documentation for more info
- */
- public CertPath engineGenerateCertPath(
- InputStream inStream, String encoding) throws CertificateException {
- if (inStream == null) {
- throw new CertificateException("inStream == null");
- }
- if (!inStream.markSupported()) {
- inStream = new RestoringInputStream(inStream);
- }
- try {
- inStream.mark(1);
- int ch;
-
- // check if it is PEM encoded form
- if ((ch = inStream.read()) == '-') {
- // decode PEM chunk into ASN.1 form and decode CertPath object
- return X509CertPathImpl.getInstance(
- decodePEM(inStream, FREE_BOUND_SUFFIX), encoding);
- } else if (ch == 0x30) { // ASN.1 Sequence
- inStream.reset();
- // decode ASN.1 form
- return X509CertPathImpl.getInstance(inStream, encoding);
- } else {
- throw new CertificateException("Unsupported encoding");
- }
- } catch (IOException e) {
- throw new CertificateException(e);
- }
- }
-
- /**
- * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(List)
- * method documentation for more info
- */
- public CertPath engineGenerateCertPath(List<? extends Certificate> certificates)
- throws CertificateException {
- return new X509CertPathImpl(certificates);
- }
-
- /**
- * @see java.security.cert.CertificateFactorySpi#engineGetCertPathEncodings()
- * method documentation for more info
- */
- public Iterator<String> engineGetCertPathEncodings() {
- return X509CertPathImpl.encodings.iterator();
- }
-
- // ---------------------------------------------------------------------
- // ------------------------ Staff methods ------------------------------
- // ---------------------------------------------------------------------
-
- private static final byte[] PEM_BEGIN = "-----BEGIN".getBytes(StandardCharsets.UTF_8);
- private static final byte[] PEM_END = "-----END".getBytes(StandardCharsets.UTF_8);
- /**
- * Code describing free format for PEM boundary suffix:
- * "^-----BEGIN.*\n" at the beginning, and<br>
- * "\n-----END.*(EOF|\n)$" at the end.
- */
- private static final byte[] FREE_BOUND_SUFFIX = null;
- /**
- * Code describing PEM boundary suffix for X.509 certificate:
- * "^-----BEGIN CERTIFICATE-----\n" at the beginning, and<br>
- * "\n-----END CERTIFICATE-----" at the end.
- */
- private static final byte[] CERT_BOUND_SUFFIX = " CERTIFICATE-----".getBytes(StandardCharsets.UTF_8);
-
- /**
- * Method retrieves the PEM encoded data from the stream
- * and returns its decoded representation.
- * Method checks correctness of PEM boundaries. It supposes that
- * the first '-' of the opening boundary has already been read from
- * the stream. So first of all it checks that the leading bytes
- * are equal to "-----BEGIN" boundary prefix. Than if boundary_suffix
- * is not null, it checks that next bytes equal to boundary_suffix
- * + new line char[s] ([CR]LF).
- * If boundary_suffix parameter is null, method supposes free suffix
- * format and skips any bytes until the new line.<br>
- * After the opening boundary has been read and checked, the method
- * read Base64 encoded data until closing PEM boundary is not reached.<br>
- * Than it checks closing boundary - it should start with new line +
- * "-----END" + boundary_suffix. If boundary_suffix is null,
- * any characters are skipped until the new line.<br>
- * After this any trailing new line characters are skipped from the stream,
- * Base64 encoding is decoded and returned.
- * @param inStream the stream containing the PEM encoding.
- * @param boundary_suffix the suffix of expected PEM multipart
- * boundary delimiter.<br>
- * If it is null, that any character sequences are accepted.
- * @throws IOException If PEM boundary delimiter does not comply
- * with expected or some I/O or decoding problems occur.
- */
- private byte[] decodePEM(InputStream inStream, byte[] boundary_suffix)
- throws IOException {
- int ch; // the char to be read
- // check and skip opening boundary delimiter
- // (first '-' is supposed as already read)
- for (int i = 1; i < PEM_BEGIN.length; ++i) {
- if (PEM_BEGIN[i] != (ch = inStream.read())) {
- throw new IOException(
- "Incorrect PEM encoding: '-----BEGIN"
- + ((boundary_suffix == null)
- ? "" : new String(boundary_suffix))
- + "' is expected as opening delimiter boundary.");
- }
- }
- if (boundary_suffix == null) {
- // read (skip) the trailing characters of
- // the beginning PEM boundary delimiter
- while ((ch = inStream.read()) != '\n') {
- if (ch == -1) {
- throw new IOException("Incorrect PEM encoding: EOF before content");
- }
- }
- } else {
- for (int i=0; i<boundary_suffix.length; i++) {
- if (boundary_suffix[i] != inStream.read()) {
- throw new IOException("Incorrect PEM encoding: '-----BEGIN" +
- new String(boundary_suffix) + "' is expected as opening delimiter boundary.");
- }
- }
- // read new line characters
- if ((ch = inStream.read()) == '\r') {
- // CR has been read, now read LF character
- ch = inStream.read();
- }
- if (ch != '\n') {
- throw new IOException("Incorrect PEM encoding: newline expected after " +
- "opening delimiter boundary");
- }
- }
- int size = 1024; // the size of the buffer containing Base64 data
- byte[] buff = new byte[size];
- int index = 0;
- // read bytes while ending boundary delimiter is not reached
- while ((ch = inStream.read()) != '-') {
- if (ch == -1) {
- throw new IOException("Incorrect Base64 encoding: EOF without closing delimiter");
- }
- buff[index++] = (byte) ch;
- if (index == size) {
- // enlarge the buffer
- byte[] newbuff = new byte[size+1024];
- System.arraycopy(buff, 0, newbuff, 0, size);
- buff = newbuff;
- size += 1024;
- }
- }
- if (buff[index-1] != '\n') {
- throw new IOException("Incorrect Base64 encoding: newline expected before " +
- "closing boundary delimiter");
- }
- // check and skip closing boundary delimiter prefix
- // (first '-' was read)
- for (int i = 1; i < PEM_END.length; ++i) {
- if (PEM_END[i] != inStream.read()) {
- throw badEnd(boundary_suffix);
- }
- }
- if (boundary_suffix == null) {
- // read (skip) the trailing characters of
- // the closing PEM boundary delimiter
- while (((ch = inStream.read()) != -1) && (ch != '\n') && (ch != '\r')) {
- }
- } else {
- for (int i=0; i<boundary_suffix.length; i++) {
- if (boundary_suffix[i] != inStream.read()) {
- throw badEnd(boundary_suffix);
- }
- }
- }
- // skip trailing line breaks
- inStream.mark(1);
- while (((ch = inStream.read()) != -1) && (ch == '\n' || ch == '\r')) {
- inStream.mark(1);
- }
- inStream.reset();
- buff = Base64.decode(buff, index);
- if (buff == null) {
- throw new IOException("Incorrect Base64 encoding");
- }
- return buff;
- }
-
- private IOException badEnd(byte[] boundary_suffix) throws IOException {
- String s = (boundary_suffix == null) ? "" : new String(boundary_suffix);
- throw new IOException("Incorrect PEM encoding: '-----END" + s + "' is expected as closing delimiter boundary.");
- }
-
- /**
- * Reads the data of specified length from source
- * and returns it as an array.
- * @return the byte array contained read data or
- * null if the stream contains not enough data
- * @throws IOException if some I/O error has been occurred.
- */
- private static byte[] readBytes(InputStream source, int length)
- throws IOException {
- byte[] result = new byte[length];
- for (int i=0; i<length; i++) {
- int bytik = source.read();
- if (bytik == -1) {
- return null;
- }
- result[i] = (byte) bytik;
- }
- return result;
- }
-
- /**
- * Returns the Certificate object corresponding to the provided encoding.
- * Resulting object is retrieved from the cache
- * if it contains such correspondence
- * and is constructed on the base of encoding
- * and stored in the cache otherwise.
- * @throws IOException if some decoding errors occur
- * (in the case of cache miss).
- */
- private static Certificate getCertificate(byte[] encoding)
- throws CertificateException, IOException {
- if (encoding.length < CERT_CACHE_SEED_LENGTH) {
- throw new CertificateException("encoding.length < CERT_CACHE_SEED_LENGTH");
- }
- synchronized (CERT_CACHE) {
- long hash = CERT_CACHE.getHash(encoding);
- if (CERT_CACHE.contains(hash)) {
- Certificate res =
- (Certificate) CERT_CACHE.get(hash, encoding);
- if (res != null) {
- return res;
- }
- }
- Certificate res = new X509CertImpl(encoding);
- CERT_CACHE.put(hash, encoding, res);
- return res;
- }
- }
-
- /**
- * Returns the Certificate object corresponding to the encoding provided
- * by the stream.
- * Resulting object is retrieved from the cache
- * if it contains such correspondence
- * and is constructed on the base of encoding
- * and stored in the cache otherwise.
- * @throws IOException if some decoding errors occur
- * (in the case of cache miss).
- */
- private static Certificate getCertificate(InputStream inStream)
- throws CertificateException, IOException {
- synchronized (CERT_CACHE) {
- inStream.mark(CERT_CACHE_SEED_LENGTH);
- // read the prefix of the encoding
- byte[] buff = readBytes(inStream, CERT_CACHE_SEED_LENGTH);
- inStream.reset();
- if (buff == null) {
- throw new CertificateException("InputStream doesn't contain enough data");
- }
- long hash = CERT_CACHE.getHash(buff);
- if (CERT_CACHE.contains(hash)) {
- byte[] encoding = new byte[BerInputStream.getLength(buff)];
- if (encoding.length < CERT_CACHE_SEED_LENGTH) {
- throw new CertificateException("Bad Certificate encoding");
- }
- Streams.readFully(inStream, encoding);
- Certificate res = (Certificate) CERT_CACHE.get(hash, encoding);
- if (res != null) {
- return res;
- }
- res = new X509CertImpl(encoding);
- CERT_CACHE.put(hash, encoding, res);
- return res;
- } else {
- inStream.reset();
- Certificate res = new X509CertImpl(inStream);
- CERT_CACHE.put(hash, res.getEncoded(), res);
- return res;
- }
- }
- }
-
- /**
- * Returns the CRL object corresponding to the provided encoding.
- * Resulting object is retrieved from the cache
- * if it contains such correspondence
- * and is constructed on the base of encoding
- * and stored in the cache otherwise.
- * @throws IOException if some decoding errors occur
- * (in the case of cache miss).
- */
- private static CRL getCRL(byte[] encoding)
- throws CRLException, IOException {
- if (encoding.length < CRL_CACHE_SEED_LENGTH) {
- throw new CRLException("encoding.length < CRL_CACHE_SEED_LENGTH");
- }
- synchronized (CRL_CACHE) {
- long hash = CRL_CACHE.getHash(encoding);
- if (CRL_CACHE.contains(hash)) {
- X509CRL res = (X509CRL) CRL_CACHE.get(hash, encoding);
- if (res != null) {
- return res;
- }
- }
- X509CRL res = new X509CRLImpl(encoding);
- CRL_CACHE.put(hash, encoding, res);
- return res;
- }
- }
-
- /**
- * Returns the CRL object corresponding to the encoding provided
- * by the stream.
- * Resulting object is retrieved from the cache
- * if it contains such correspondence
- * and is constructed on the base of encoding
- * and stored in the cache otherwise.
- * @throws IOException if some decoding errors occur
- * (in the case of cache miss).
- */
- private static CRL getCRL(InputStream inStream)
- throws CRLException, IOException {
- synchronized (CRL_CACHE) {
- inStream.mark(CRL_CACHE_SEED_LENGTH);
- byte[] buff = readBytes(inStream, CRL_CACHE_SEED_LENGTH);
- // read the prefix of the encoding
- inStream.reset();
- if (buff == null) {
- throw new CRLException("InputStream doesn't contain enough data");
- }
- long hash = CRL_CACHE.getHash(buff);
- if (CRL_CACHE.contains(hash)) {
- byte[] encoding = new byte[BerInputStream.getLength(buff)];
- if (encoding.length < CRL_CACHE_SEED_LENGTH) {
- throw new CRLException("Bad CRL encoding");
- }
- Streams.readFully(inStream, encoding);
- CRL res = (CRL) CRL_CACHE.get(hash, encoding);
- if (res != null) {
- return res;
- }
- res = new X509CRLImpl(encoding);
- CRL_CACHE.put(hash, encoding, res);
- return res;
- } else {
- X509CRL res = new X509CRLImpl(inStream);
- CRL_CACHE.put(hash, res.getEncoded(), res);
- return res;
- }
- }
- }
-
- /*
- * This class extends any existing input stream with
- * mark functionality. It acts as a wrapper over the
- * stream and supports reset to the
- * marked state with readlimit no more than BUFF_SIZE.
- */
- private static class RestoringInputStream extends InputStream {
-
- // wrapped input stream
- private final InputStream inStream;
- // specifies how much of the read data is buffered
- // after the mark has been set up
- private static final int BUFF_SIZE = 32;
- // buffer to keep the bytes read after the mark has been set up
- private final int[] buff = new int[BUFF_SIZE*2];
- // position of the next byte to read,
- // the value of -1 indicates that the buffer is not used
- // (mark was not set up or was invalidated, or reset to the marked
- // position has been done and all the buffered data was read out)
- private int pos = -1;
- // position of the last buffered byte
- private int bar = 0;
- // position in the buffer where the mark becomes invalidated
- private int end = 0;
-
- /**
- * Creates the mark supporting wrapper over the stream.
- */
- public RestoringInputStream(InputStream inStream) {
- this.inStream = inStream;
- }
-
- @Override
- public int available() throws IOException {
- return (bar - pos) + inStream.available();
- }
-
- @Override
- public void close() throws IOException {
- inStream.close();
- }
-
- @Override
- public void mark(int readlimit) {
- if (pos < 0) {
- pos = 0;
- bar = 0;
- end = BUFF_SIZE - 1;
- } else {
- end = (pos + BUFF_SIZE - 1) % BUFF_SIZE;
- }
- }
-
- @Override
- public boolean markSupported() {
- return true;
- }
-
- /**
- * Reads the byte from the stream. If mark has been set up
- * and was not invalidated byte is read from the underlying
- * stream and saved into the buffer. If the current read position
- * has been reset to the marked position and there are remaining
- * bytes in the buffer, the byte is taken from it. In the other cases
- * (if mark has been invalidated, or there are no buffered bytes)
- * the byte is taken directly from the underlying stream and it is
- * returned without saving to the buffer.
- *
- * @see java.io.InputStream#read()
- * method documentation for more info
- */
- public int read() throws IOException {
- // if buffer is currently used
- if (pos >= 0) {
- // current position in the buffer
- int cur = pos % BUFF_SIZE;
- // check whether the buffer contains the data to be read
- if (cur < bar) {
- // return the data from the buffer
- pos++;
- return buff[cur];
- }
- // check whether buffer has free space
- if (cur != end) {
- // it has, so read the data from the wrapped stream
- // and place it in the buffer
- buff[cur] = inStream.read();
- bar = cur+1;
- pos++;
- return buff[cur];
- } else {
- // buffer if full and can not operate
- // any more, so invalidate the mark position
- // and turn off the using of buffer
- pos = -1;
- }
- }
- // buffer is not used, so return the data from the wrapped stream
- return inStream.read();
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int read_b;
- int i;
- for (i=0; i<len; i++) {
- if ((read_b = read()) == -1) {
- return (i == 0) ? -1 : i;
- }
- b[off+i] = (byte) read_b;
- }
- return i;
- }
-
- @Override
- public void reset() throws IOException {
- if (pos >= 0) {
- pos = (end + 1) % BUFF_SIZE;
- } else {
- throw new IOException("Could not reset the stream: " +
- "position became invalid or stream has not been marked");
- }
- }
- }
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
deleted file mode 100644
index 4600bdc..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-/**
-* @author Alexander Y. Kleymenov
-* @version $Revision$
-*/
-
-package org.apache.harmony.security.provider.cert;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Principal;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Set;
-import javax.security.auth.x500.X500Principal;
-import org.apache.harmony.security.utils.AlgNameMapper;
-import org.apache.harmony.security.x509.Certificate;
-import org.apache.harmony.security.x509.Extension;
-import org.apache.harmony.security.x509.Extensions;
-import org.apache.harmony.security.x509.TBSCertificate;
-
-/**
- * This class is an implementation of X509Certificate. It wraps
- * the instance of org.apache.harmony.security.x509.Certificate
- * built on the base of provided ASN.1 DER encoded form of
- * Certificate structure (as specified in RFC 3280
- * http://www.ietf.org/rfc/rfc3280.txt).
- * @see org.apache.harmony.security.x509.Certificate
- * @see java.security.cert.X509Certificate
- */
-public final class X509CertImpl extends X509Certificate {
-
- /** @serial */
- private static final long serialVersionUID = 2972248729446736154L;
-
- /** the core object to be wrapped in X509Certificate */
- private final Certificate certificate;
-
- private final TBSCertificate tbsCert;
- private final Extensions extensions;
- // to speed up access to the info, the following fields
- // cache values retrieved from the certificate object,
- // initialized using the "single-check idiom".
- private volatile long notBefore = -1;
- private volatile long notAfter = -1;
- private volatile BigInteger serialNumber;
- private volatile X500Principal issuer;
- private volatile X500Principal subject;
- private volatile byte[] tbsCertificate;
- private volatile byte[] signature;
- private volatile String sigAlgName;
- private volatile String sigAlgOID;
- private volatile byte[] sigAlgParams;
- // indicates whether the signature algorithm parameters are null
- private volatile boolean nullSigAlgParams;
- private volatile PublicKey publicKey;
-
- // encoding of the certificate
- private volatile byte[] encoding;
-
- /**
- * Constructs the instance on the base of ASN.1 encoded
- * form of X.509 certificate provided via stream parameter.
- * @param in input stream containing ASN.1 encoded form of certificate.
- * @throws CertificateException if some decoding problems occur.
- */
- public X509CertImpl(InputStream in) throws CertificateException {
- try {
- // decode the Certificate object
- this.certificate = (Certificate) Certificate.ASN1.decode(in);
- // cache the values of TBSCertificate and Extensions
- this.tbsCert = certificate.getTbsCertificate();
- this.extensions = tbsCert.getExtensions();
- } catch (IOException e) {
- throw new CertificateException(e);
- }
- }
-
- /**
- * Constructs the instance on the base of existing Certificate object to
- * be wrapped.
- */
- public X509CertImpl(Certificate certificate) {
- this.certificate = certificate;
- // cache the values of TBSCertificate and Extensions
- this.tbsCert = certificate.getTbsCertificate();
- this.extensions = tbsCert.getExtensions();
- }
-
- /**
- * Constructs the instance on the base of ASN.1 encoded
- * form of X.509 certificate provided via array of bytes.
- * @param encoding byte array containing ASN.1 encoded form of certificate.
- * @throws IOException if some decoding problems occur.
- */
- public X509CertImpl(byte[] encoding) throws IOException {
- this((Certificate) Certificate.ASN1.decode(encoding));
- }
-
- public void checkValidity()
- throws CertificateExpiredException, CertificateNotYetValidException {
- checkValidity(System.currentTimeMillis());
- }
-
- public void checkValidity(Date date)
- throws CertificateExpiredException, CertificateNotYetValidException {
- checkValidity(date.getTime());
- }
-
- private void checkValidity(long time)
- throws CertificateExpiredException, CertificateNotYetValidException {
- if (time < getNotBeforeInternal()) {
- throw new CertificateNotYetValidException("current time: " + new Date(time)
- + ", validation time: " + new Date(getNotBeforeInternal()));
- }
- if (time > getNotAfterInternal()) {
- throw new CertificateExpiredException("current time: " + new Date(time)
- + ", expiration time: " + new Date(getNotAfterInternal()));
- }
- }
-
- public int getVersion() {
- return tbsCert.getVersion() + 1;
- }
-
- public BigInteger getSerialNumber() {
- BigInteger result = serialNumber;
- if (result == null) {
- serialNumber = result = tbsCert.getSerialNumber();
- }
- return result;
- }
-
- public Principal getIssuerDN() {
- return getIssuerX500Principal();
- }
-
- public X500Principal getIssuerX500Principal() {
- X500Principal result = issuer;
- if (result == null) {
- // retrieve the issuer's principal
- issuer = result = tbsCert.getIssuer().getX500Principal();
- }
- return result;
- }
-
- public Principal getSubjectDN() {
- return getSubjectX500Principal();
- }
-
- public X500Principal getSubjectX500Principal() {
- X500Principal result = subject;
- if (result == null) {
- // retrieve the subject's principal
- subject = result = tbsCert.getSubject().getX500Principal();
- }
- return result;
- }
-
- public Date getNotBefore() {
- return new Date(getNotBeforeInternal());
- }
-
- private long getNotBeforeInternal() {
- long result = notBefore;
- if (result == -1) {
- notBefore = result = tbsCert.getValidity().getNotBefore().getTime();
- }
- return result;
- }
-
- public Date getNotAfter() {
- return new Date(getNotAfterInternal());
- }
-
- private long getNotAfterInternal() {
- long result = notAfter;
- if (result == -1) {
- notAfter = result = tbsCert.getValidity().getNotAfter().getTime();
- }
- return result;
- }
-
- public byte[] getTBSCertificate() throws CertificateEncodingException {
- return getTbsCertificateInternal().clone();
- }
-
- private byte[] getTbsCertificateInternal() {
- byte[] result = tbsCertificate;
- if (result == null) {
- tbsCertificate = result = tbsCert.getEncoded();
- }
- return result;
- }
-
- public byte[] getSignature() {
- return getSignatureInternal().clone();
- }
-
- private byte[] getSignatureInternal() {
- byte[] result = signature;
- if (result == null) {
- signature = result = certificate.getSignatureValue();
- }
- return result;
- }
-
- public String getSigAlgName() {
- String result = sigAlgName;
- if (result == null) {
- String sigAlgOIDLocal = getSigAlgOID();
- // retrieve the name of the signing algorithm
- result = AlgNameMapper.map2AlgName(sigAlgOIDLocal);
- if (result == null) {
- // if could not be found, use OID as a name
- result = sigAlgOIDLocal;
- }
- sigAlgName = result;
- }
- return result;
- }
-
- public String getSigAlgOID() {
- String result = sigAlgOID;
- if (result == null) {
- // if info was not retrieved (and cached), do it:
- sigAlgOID = result = tbsCert.getSignature().getAlgorithm();
- }
- return result;
- }
-
- public byte[] getSigAlgParams() {
- if (nullSigAlgParams) {
- return null;
- }
- byte[] result = sigAlgParams;
- if (result == null) {
- result = tbsCert.getSignature().getParameters();
- if (result == null) {
- nullSigAlgParams = true;
- return null;
- }
- sigAlgParams = result;
- }
- return result;
- }
-
- public boolean[] getIssuerUniqueID() {
- return tbsCert.getIssuerUniqueID();
- }
-
- public boolean[] getSubjectUniqueID() {
- return tbsCert.getSubjectUniqueID();
- }
-
- public boolean[] getKeyUsage() {
- if (extensions == null) {
- return null;
- }
- return extensions.valueOfKeyUsage();
- }
-
- public List<String> getExtendedKeyUsage()
- throws CertificateParsingException {
- if (extensions == null) {
- return null;
- }
- try {
- return extensions.valueOfExtendedKeyUsage();
- } catch (IOException e) {
- throw new CertificateParsingException(e);
- }
- }
-
- public int getBasicConstraints() {
- if (extensions == null) {
- return -1;
- }
- return extensions.valueOfBasicConstraints();
- }
-
- public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
- if (extensions == null) {
- return null;
- }
- try {
- // Retrieve the extension value from the cached extensions object
- // This extension is not checked for correctness during
- // certificate generation, so now it can throw exception
- return extensions.valueOfSubjectAlternativeName();
- } catch (IOException e) {
- throw new CertificateParsingException(e);
- }
- }
-
- /**
- * @see java.security.cert.X509Certificate#getIssuerAlternativeNames()
- * method documentation for more information.
- */
- public Collection<List<?>> getIssuerAlternativeNames() throws CertificateParsingException {
- if (extensions == null) {
- return null;
- }
- try {
- // Retrieve the extension value from the cached extensions object
- // This extension is not checked for correctness during
- // certificate generation, so now it can throw exception
- return extensions.valueOfIssuerAlternativeName();
- } catch (IOException e) {
- throw new CertificateParsingException(e);
- }
- }
-
- @Override public byte[] getEncoded() throws CertificateEncodingException {
- return getEncodedInternal().clone();
- }
- private byte[] getEncodedInternal() throws CertificateEncodingException {
- byte[] result = encoding;
- if (encoding == null) {
- encoding = result = certificate.getEncoded();
- }
- return result;
- }
-
- @Override public PublicKey getPublicKey() {
- PublicKey result = publicKey;
- if (result == null) {
- publicKey = result = tbsCert.getSubjectPublicKeyInfo().getPublicKey();
- }
- return result;
- }
-
- @Override public String toString() {
- return certificate.toString();
- }
-
- @Override public void verify(PublicKey key)
- throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
- NoSuchProviderException, SignatureException {
-
- Signature signature = Signature.getInstance(getSigAlgName());
- signature.initVerify(key);
- // retrieve the encoding of the TBSCertificate structure
- byte[] tbsCertificateLocal = getTbsCertificateInternal();
- // compute and verify the signature
- signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length);
- if (!signature.verify(certificate.getSignatureValue())) {
- throw new SignatureException("Signature was not verified");
- }
- }
-
- @Override public void verify(PublicKey key, String sigProvider)
- throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
- NoSuchProviderException, SignatureException {
-
- Signature signature = Signature.getInstance(getSigAlgName(), sigProvider);
- signature.initVerify(key);
- // retrieve the encoding of the TBSCertificate structure
- byte[] tbsCertificateLocal = getTbsCertificateInternal();
- // compute and verify the signature
- signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length);
- if (!signature.verify(certificate.getSignatureValue())) {
- throw new SignatureException("Signature was not verified");
- }
- }
-
- @Override public Set<String> getNonCriticalExtensionOIDs() {
- if (extensions == null) {
- return null;
- }
- // retrieve the info from the cached extensions object
- return extensions.getNonCriticalExtensions();
- }
-
- @Override public Set<String> getCriticalExtensionOIDs() {
- if (extensions == null) {
- return null;
- }
- // retrieve the info from the cached extensions object
- return extensions.getCriticalExtensions();
- }
-
- @Override public byte[] getExtensionValue(String oid) {
- if (extensions == null) {
- return null;
- }
- // retrieve the info from the cached extensions object
- Extension ext = extensions.getExtensionByOID(oid);
- return (ext == null) ? null : ext.getRawExtnValue();
- }
-
- @Override public boolean hasUnsupportedCriticalExtension() {
- if (extensions == null) {
- return false;
- }
- // retrieve the info from the cached extensions object
- return extensions.hasUnsupportedCritical();
- }
-
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertPathImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertPathImpl.java
deleted file mode 100644
index 3699700..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertPathImpl.java
+++ /dev/null
@@ -1,451 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-/**
-* @author Alexander Y. Kleymenov
-* @version $Revision$
-*/
-
-package org.apache.harmony.security.provider.cert;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.cert.CertPath;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.harmony.security.asn1.ASN1Any;
-import org.apache.harmony.security.asn1.ASN1Explicit;
-import org.apache.harmony.security.asn1.ASN1Implicit;
-import org.apache.harmony.security.asn1.ASN1Oid;
-import org.apache.harmony.security.asn1.ASN1Sequence;
-import org.apache.harmony.security.asn1.ASN1SequenceOf;
-import org.apache.harmony.security.asn1.ASN1Type;
-import org.apache.harmony.security.asn1.BerInputStream;
-import org.apache.harmony.security.pkcs7.ContentInfo;
-import org.apache.harmony.security.pkcs7.SignedData;
-import org.apache.harmony.security.x509.Certificate;
-
-/**
- * This class is an implementation of X.509 CertPath. This implementation
- * provides ability to create the instance of X.509 Certification Path
- * by several means:<br>
- *
- * 1. It can be created over the list of X.509 certificates
- * (implementations of X509Certificate class) provided in constructor.<br>
- *
- * 2. It can be created by means of <code>getInstance</code> methods
- * on the base of the following ASN.1 DER encoded forms:<br>
- *
- * - PkiPath as defined in
- * ITU-T Recommendation X.509(2000) Corrigendum 1(2001)
- * (can be seen at
- * ftp://ftp.bull.com/pub/OSIdirectory/DefectResolution/TechnicalCorrigenda/ApprovedTechnicalCorrigendaToX.509/8%7CX.509-TC1(4th).pdf)
- * <br>
- * - PKCS #7 SignedData object provided in the form of
- * ContentInfo structure. CertPath object is generated on the base of
- * certificates presented in <code>certificates</code> field of the SignedData
- * object which in its turn is retrieved from ContentInfo structure.
- * (see http://www.ietf.org/rfc/rfc2315.txt
- * for more info on PKCS #7)
- * <br>
- *
- */
-public class X509CertPathImpl extends CertPath {
- /**
- * @serial
- */
- private static final long serialVersionUID = 7989755106209515436L;
-
- /**
- * Supported encoding types for CerthPath. Used by the various APIs that
- * encode this into bytes such as {@link #getEncoded()}.
- */
- private enum Encoding {
- PKI_PATH("PkiPath"),
- PKCS7("PKCS7");
-
- private final String apiName;
-
- Encoding(String apiName) {
- this.apiName = apiName;
- }
-
- static Encoding findByApiName(String apiName) throws CertificateEncodingException {
- for (Encoding element : values()) {
- if (element.apiName.equals(apiName)) {
- return element;
- }
- }
-
- return null;
- }
- }
-
- /** Unmodifiable list of encodings for the API. */
- static final List<String> encodings = Collections.unmodifiableList(Arrays.asList(new String[] {
- Encoding.PKI_PATH.apiName,
- Encoding.PKCS7.apiName,
- }));
-
- /** The list of certificates in the order of target toward trust anchor. */
- private final List<X509Certificate> certificates;
-
- /** PkiPath encoding of the certification path. */
- private byte[] pkiPathEncoding;
-
- /** PKCS7 encoding of the certification path. */
- private byte[] pkcs7Encoding;
-
- /**
- * Creates an instance of X.509 CertPath over the specified list of
- * certificates.
- *
- * @throws CertificateException if some of the object in the list is not an
- * instance of subclass of X509Certificate.
- */
- public X509CertPathImpl(List<? extends java.security.cert.Certificate> certs)
- throws CertificateException {
- super("X.509");
-
- final int size = certs.size();
- certificates = new ArrayList<X509Certificate>(size);
-
- for (int i = 0; i < size; i++) {
- final java.security.cert.Certificate cert = certs.get(i);
- if (!(cert instanceof X509Certificate)) {
- throw new CertificateException("Certificate " + i + " is not an X.509 certificate");
- }
-
- certificates.add((X509Certificate) cert);
- }
- }
-
- /**
- * Creates an X.509 CertPath over the specified {@code certs}. The
- * {@code certs} should be sorted correctly when calling into the
- * constructor. Additionally, the {@code encodedPath} should match the
- * expected output for the {@code type} of encoding.
- */
- private X509CertPathImpl(List<X509Certificate> certs, Encoding type) {
- super("X.509");
-
- certificates = certs;
- }
-
- /**
- * Extract a CertPath from a PKCS#7 {@code contentInfo} object.
- */
- private static X509CertPathImpl getCertPathFromContentInfo(ContentInfo contentInfo)
- throws CertificateException {
- final SignedData sd = contentInfo.getSignedData();
- if (sd == null) {
- throw new CertificateException("Incorrect PKCS7 encoded form: missing signed data");
- }
-
- List<Certificate> certs = sd.getCertificates();
- if (certs == null) {
- certs = Collections.emptyList();
- }
-
- final List<X509Certificate> result = new ArrayList<X509Certificate>(certs.size());
- for (Certificate cert : certs) {
- result.add(new X509CertImpl(cert));
- }
-
- return new X509CertPathImpl(result, Encoding.PKCS7);
- }
-
- /**
- * Generates certification path object on the base of PkiPath encoded form
- * provided via input stream.
- *
- * @throws CertificateException if some problems occurred during the
- * decoding.
- */
- public static X509CertPathImpl getInstance(InputStream in) throws CertificateException {
- try {
- return (X509CertPathImpl) ASN1.decode(in);
- } catch (IOException e) {
- throw new CertificateException("Failed to decode CertPath", e);
- }
- }
-
- /**
- * Generates certification path object on the basis of encoding provided via
- * input stream. The format of provided encoded form is specified by
- * parameter <code>encoding</code>.
- *
- * @throws CertificateException if specified encoding form is not supported,
- * or some problems occurred during the decoding.
- */
- public static X509CertPathImpl getInstance(InputStream in, String encoding)
- throws CertificateException {
- try {
- final Encoding encType = Encoding.findByApiName(encoding);
- if (encType == null) {
- throw new CertificateException("Unsupported encoding: " + encoding);
- }
-
- switch (encType) {
- case PKI_PATH:
- return (X509CertPathImpl) ASN1.decode(in);
- case PKCS7:
- return getCertPathFromContentInfo((ContentInfo) ContentInfo.ASN1.decode(in));
- default:
- throw new CertificateException("Unsupported encoding: " + encoding);
- }
- } catch (IOException e) {
- throw new CertificateException("Failed to decode CertPath", e);
- }
- }
-
- /**
- * Generates certification path object on the base of PkiPath
- * encoded form provided via array of bytes.
- * @throws CertificateException if some problems occurred during
- * the decoding.
- */
- public static X509CertPathImpl getInstance(byte[] in) throws CertificateException {
- try {
- return (X509CertPathImpl) ASN1.decode(in);
- } catch (IOException e) {
- throw new CertificateException("Failed to decode CertPath", e);
- }
- }
-
- /**
- * Generates certification path object on the base of encoding provided via
- * array of bytes. The format of provided encoded form is specified by
- * parameter {@code encoding}.
- *
- * @throws CertificateException if specified encoding form is not supported,
- * or some problems occurred during the decoding.
- */
- public static X509CertPathImpl getInstance(byte[] in, String encoding)
- throws CertificateException {
- try {
- final Encoding encType = Encoding.findByApiName(encoding);
- if (encType == null) {
- throw new CertificateException("Unsupported encoding: " + encoding);
- }
-
- switch (encType) {
- case PKI_PATH:
- return (X509CertPathImpl) ASN1.decode(in);
- case PKCS7:
- return getCertPathFromContentInfo((ContentInfo) ContentInfo.ASN1.decode(in));
- default:
- throw new CertificateException("Unsupported encoding: " + encoding);
- }
- } catch (IOException e) {
- throw new CertificateException("Failed to decode CertPath", e);
- }
- }
-
- // ---------------------------------------------------------------------
- // ---- java.security.cert.CertPath abstract method implementations ----
- // ---------------------------------------------------------------------
-
- /**
- * @see java.security.cert.CertPath#getCertificates()
- * method documentation for more info
- */
- @Override
- public List<X509Certificate> getCertificates() {
- return Collections.unmodifiableList(certificates);
- }
-
- /**
- * Returns in PkiPath format which is our default encoding.
- *
- * @see java.security.cert.CertPath#getEncoded()
- */
- @Override
- public byte[] getEncoded() throws CertificateEncodingException {
- return getEncoded(Encoding.PKI_PATH);
- }
-
- /**
- * @see #getEncoded(String)
- */
- private byte[] getEncoded(Encoding encoding) throws CertificateEncodingException {
- switch (encoding) {
- case PKI_PATH:
- if (pkiPathEncoding == null) {
- pkiPathEncoding = ASN1.encode(this);
- }
-
- return pkiPathEncoding.clone();
- case PKCS7:
- if (pkcs7Encoding == null) {
- pkcs7Encoding = PKCS7_SIGNED_DATA_OBJECT.encode(this);
- }
-
- return pkcs7Encoding.clone();
- default:
- throw new CertificateEncodingException("Unsupported encoding: " + encoding);
- }
- }
-
- /**
- * @see java.security.cert.CertPath#getEncoded(String)
- */
- @Override
- public byte[] getEncoded(String encoding) throws CertificateEncodingException {
- final Encoding encType = Encoding.findByApiName(encoding);
- if (encType == null) {
- throw new CertificateEncodingException("Unsupported encoding: " + encoding);
- }
-
- return getEncoded(encType);
- }
-
- /**
- * @see java.security.cert.CertPath#getEncodings()
- * method documentation for more info
- */
- @Override
- public Iterator<String> getEncodings() {
- return encodings.iterator();
- }
-
- /**
- * ASN.1 DER Encoder/Decoder for PkiPath structure.
- */
- public static final ASN1SequenceOf ASN1 = new ASN1SequenceOf(ASN1Any.getInstance()) {
- /**
- * Builds the instance of X509CertPathImpl on the base of the list of
- * ASN.1 encodings of X.509 certificates provided via PkiPath structure.
- * This method participates in decoding process.
- */
- public Object getDecodedObject(BerInputStream in) throws IOException {
- // retrieve the decoded content
- final List<byte[]> encodedCerts = (List<byte[]>) in.content;
-
- final int size = encodedCerts.size();
- final List<X509Certificate> certificates = new ArrayList<X509Certificate>(size);
-
- for (int i = size - 1; i >= 0; i--) {
- // create the X.509 certificate on the base of its encoded form
- // and add it to the list.
- certificates.add(new X509CertImpl((Certificate) Certificate.ASN1
- .decode(encodedCerts.get(i))));
- }
-
- // create and return the resulting object
- return new X509CertPathImpl(certificates, Encoding.PKI_PATH);
- }
-
- /**
- * Returns the Collection of the encoded form of certificates contained
- * in the X509CertPathImpl object to be encoded.
- * This method participates in encoding process.
- */
- public Collection<byte[]> getValues(Object object) {
- // object to be encoded
- final X509CertPathImpl cp = (X509CertPathImpl) object;
-
- // if it has no certificates in it - create the sequence of size 0
- if (cp.certificates == null) {
- return Collections.emptyList();
- }
-
- final int size = cp.certificates.size();
- final List<byte[]> encodings = new ArrayList<byte[]>(size);
-
- try {
- for (int i = size - 1; i >= 0; i--) {
- // get the encoded form of certificate and place it into the
- // list to be encoded in PkiPath format
- encodings.add(cp.certificates.get(i).getEncoded());
- }
- } catch (CertificateEncodingException e) {
- throw new IllegalArgumentException("Encoding error occurred", e);
- }
-
- return encodings;
- }
- };
-
-
- /**
- * Encoder for PKCS#7 SignedData. It is assumed that only certificate field
- * is important all other fields contain pre-calculated encodings.
- */
- private static final ASN1Sequence ASN1_SIGNED_DATA = new ASN1Sequence(
- new ASN1Type[] {
- // version ,digestAlgorithms, content info
- ASN1Any.getInstance(),
- // certificates
- new ASN1Implicit(0, ASN1),
- // set of crls is optional and is missed here
- ASN1Any.getInstance(),// signers info
- }) {
-
- // precalculated ASN.1 encodings for
- // version ,digestAlgorithms, content info field of SignedData
- private final byte[] PRECALCULATED_HEAD = new byte[] { 0x02, 0x01,
- 0x01,// version (v1)
- 0x31, 0x00,// empty set of DigestAlgorithms
- 0x30, 0x03, 0x06, 0x01, 0x00 // empty ContentInfo with oid=0
- };
-
- // precalculated empty set of SignerInfos
- private final byte[] SIGNERS_INFO = new byte[] { 0x31, 0x00 };
-
- protected void getValues(Object object, Object[] values) {
- values[0] = PRECALCULATED_HEAD;
- values[1] = object; // pass X509CertPathImpl object
- values[2] = SIGNERS_INFO;
- }
-
- // stub to prevent using the instance as decoder
- public Object decode(BerInputStream in) throws IOException {
- throw new RuntimeException(
- "Invalid use of encoder for PKCS#7 SignedData object");
- }
- };
-
- private static final ASN1Sequence PKCS7_SIGNED_DATA_OBJECT = new ASN1Sequence(
- new ASN1Type[] { ASN1Any.getInstance(), // contentType
- new ASN1Explicit(0, ASN1_SIGNED_DATA) // SignedData
- }) {
-
- // precalculated ASN.1 encoding for SignedData object oid
- private final byte[] SIGNED_DATA_OID = ASN1Oid.getInstance().encode(
- ContentInfo.SIGNED_DATA);
-
- protected void getValues(Object object, Object[] values) {
- values[0] = SIGNED_DATA_OID;
- values[1] = object; // pass X509CertPathImpl object
- }
-
- // stub to prevent using the instance as decoder
- public Object decode(BerInputStream in) throws IOException {
- throw new RuntimeException(
- "Invalid use of encoder for PKCS#7 SignedData object");
- }
- };
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/CryptoProvider.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/CryptoProvider.java
index 7c2785a..ad5ac7d 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/CryptoProvider.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/crypto/CryptoProvider.java
@@ -20,12 +20,9 @@
import java.security.Provider;
/**
- * Implementation of Provider for SecureRandom, MessageDigest and Signature
- * using a Secure Hash Algorithm, SHA-1;
- * see SECURE HASH STANDARD, FIPS PUB 180-1 (http://www.itl.nist.gov/fipspubs/fip180-1.htm) <BR>
- * <BR>
- * The implementation supports "SHA1PRNG", "SHA-1" and "SHA1withDSA" algorithms described in
- * JavaTM Cryptography Architecture, API Specification & Reference
+ * Implementation of Provider for SecureRandom. The implementation supports the
+ * "SHA1PRNG" algorithm described in JavaTM Cryptography Architecture, API
+ * Specification & Reference
*/
public final class CryptoProvider extends Provider {
@@ -36,46 +33,10 @@
* Creates a Provider and puts parameters
*/
public CryptoProvider() {
-
super("Crypto", 1.0, "HARMONY (SHA1 digest; SecureRandom; SHA1withDSA signature)");
- // names of classes implementing services
- final String MD_NAME = "org.apache.harmony.security.provider.crypto.SHA1_MessageDigestImpl";
- final String SR_NAME = "org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl";
-
- final String SIGN_NAME = "org.apache.harmony.security.provider.crypto.SHA1withDSA_SignatureImpl";
-
- final String SIGN_ALIAS = "SHA1withDSA";
-
-
- final String KEYF_NAME = "org.apache.harmony.security.provider.crypto.DSAKeyFactoryImpl";
-
- put("MessageDigest.SHA-1", MD_NAME);
- put("MessageDigest.SHA-1 ImplementedIn", "Software");
- put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
- put("Alg.Alias.MessageDigest.SHA", "SHA-1");
-
- put("SecureRandom.SHA1PRNG", SR_NAME);
+ put("SecureRandom.SHA1PRNG",
+ "org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl");
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
-
- put("Signature.SHA1withDSA", SIGN_NAME);
- put("Signature.SHA1withDSA ImplementedIn", "Software");
- put("Alg.Alias.Signature.SHAwithDSA", SIGN_ALIAS);
- put("Alg.Alias.Signature.DSAwithSHA1", SIGN_ALIAS);
- put("Alg.Alias.Signature.SHA1/DSA", SIGN_ALIAS);
- put("Alg.Alias.Signature.SHA/DSA", SIGN_ALIAS);
- put("Alg.Alias.Signature.SHA-1/DSA", SIGN_ALIAS);
- put("Alg.Alias.Signature.DSA", SIGN_ALIAS);
- put("Alg.Alias.Signature.DSS", SIGN_ALIAS);
-
- put("Alg.Alias.Signature.OID.1.2.840.10040.4.3", SIGN_ALIAS);
- put("Alg.Alias.Signature.1.2.840.10040.4.3", SIGN_ALIAS);
- put("Alg.Alias.Signature.1.3.14.3.2.13", SIGN_ALIAS);
- put("Alg.Alias.Signature.1.3.14.3.2.27", SIGN_ALIAS);
-
- put("KeyFactory.DSA", KEYF_NAME);
- put("KeyFactory.DSA ImplementedIn", "Software");
- put("Alg.Alias.KeyFactory.1.3.14.3.2.12", "DSA");
- put("Alg.Alias.KeyFactory.1.2.840.10040.4.1", "DSA");
}
}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAKeyFactoryImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAKeyFactoryImpl.java
deleted file mode 100644
index 690d16e..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAKeyFactoryImpl.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.harmony.security.provider.crypto;
-
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactorySpi;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-
-public class DSAKeyFactoryImpl extends KeyFactorySpi {
-
- /**
- * This method generates a DSAPrivateKey object from the provided key specification.
- *
- * @param
- * keySpec - the specification (key material) for the DSAPrivateKey.
- *
- * @return
- * a DSAPrivateKey object
- *
- * @throws InvalidKeySpecException
- * if "keySpec" is neither DSAPrivateKeySpec nor PKCS8EncodedKeySpec
- */
- protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
- throws InvalidKeySpecException {
-
- if (keySpec != null) {
- if (keySpec instanceof DSAPrivateKeySpec) {
-
- return new DSAPrivateKeyImpl((DSAPrivateKeySpec) keySpec);
- }
- if (keySpec instanceof PKCS8EncodedKeySpec) {
-
- return new DSAPrivateKeyImpl((PKCS8EncodedKeySpec) keySpec);
- }
- }
- throw new InvalidKeySpecException("'keySpec' is neither DSAPrivateKeySpec nor PKCS8EncodedKeySpec");
- }
-
- /**
- * This method generates a DSAPublicKey object from the provided key specification.
- *
- * @param
- * keySpec - the specification (key material) for the DSAPublicKey.
- *
- * @return
- * a DSAPublicKey object
- *
- * @throws InvalidKeySpecException
- * if "keySpec" is neither DSAPublicKeySpec nor X509EncodedKeySpec
- */
- protected PublicKey engineGeneratePublic(KeySpec keySpec)
- throws InvalidKeySpecException {
-
- if (keySpec != null) {
- if (keySpec instanceof DSAPublicKeySpec) {
-
- return new DSAPublicKeyImpl((DSAPublicKeySpec) keySpec);
- }
- if (keySpec instanceof X509EncodedKeySpec) {
-
- return new DSAPublicKeyImpl((X509EncodedKeySpec) keySpec);
- }
- }
- throw new InvalidKeySpecException("'keySpec' is neither DSAPublicKeySpec nor X509EncodedKeySpec");
- }
-
- /**
- * This method returns a specification for the supplied key.
- *
- * The specification will be returned in the form of an object of the type
- * specified by keySpec.
- *
- * @param key -
- * either DSAPrivateKey or DSAPublicKey
- * @param keySpec -
- * either DSAPrivateKeySpec.class or DSAPublicKeySpec.class
- *
- * @return either a DSAPrivateKeySpec or a DSAPublicKeySpec
- *
- * @throws InvalidKeySpecException
- * if "keySpec" is not a specification for DSAPublicKey or
- * DSAPrivateKey
- */
- protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
- throws InvalidKeySpecException {
-
- BigInteger p, q, g, x, y;
-
- if (key != null) {
- if (keySpec == null) {
- throw new NullPointerException("keySpec == null");
- }
- if (key instanceof DSAPrivateKey) {
- DSAPrivateKey privateKey = (DSAPrivateKey) key;
-
- if (keySpec.equals(DSAPrivateKeySpec.class)) {
-
- x = privateKey.getX();
-
- DSAParams params = privateKey.getParams();
-
- p = params.getP();
- q = params.getQ();
- g = params.getG();
-
- return (T) (new DSAPrivateKeySpec(x, p, q, g));
- }
-
- if (keySpec.equals(PKCS8EncodedKeySpec.class)) {
- return (T) (new PKCS8EncodedKeySpec(key.getEncoded()));
- }
-
- throw new InvalidKeySpecException("'keySpec' is neither DSAPrivateKeySpec nor PKCS8EncodedKeySpec");
- }
-
- if (key instanceof DSAPublicKey) {
- DSAPublicKey publicKey = (DSAPublicKey) key;
-
- if (keySpec.equals(DSAPublicKeySpec.class)) {
-
- y = publicKey.getY();
-
- DSAParams params = publicKey.getParams();
-
- p = params.getP();
- q = params.getQ();
- g = params.getG();
-
- return (T) (new DSAPublicKeySpec(y, p, q, g));
- }
-
- if (keySpec.equals(X509EncodedKeySpec.class)) {
- return (T) (new X509EncodedKeySpec(key.getEncoded()));
- }
-
- throw new InvalidKeySpecException("'keySpec' is neither DSAPublicKeySpec nor X509EncodedKeySpec");
- }
- }
- throw new InvalidKeySpecException("'key' is neither DSAPublicKey nor DSAPrivateKey");
- }
-
- /**
- * The method generates a DSAPublicKey object from the provided key.
- *
- * @param
- * key - a DSAPublicKey object or DSAPrivateKey object.
- *
- * @return
- * object of the same type as the "key" argument
- *
- * @throws InvalidKeyException
- * if "key" is neither DSAPublicKey nor DSAPrivateKey
- */
- protected Key engineTranslateKey(Key key) throws InvalidKeyException {
-
- if (key != null) {
- if (key instanceof DSAPrivateKey) {
-
- DSAPrivateKey privateKey = (DSAPrivateKey) key;
- DSAParams params = privateKey.getParams();
-
- try {
- return engineGeneratePrivate(new DSAPrivateKeySpec(
- privateKey.getX(), params.getP(), params.getQ(),
- params.getG()));
- } catch (InvalidKeySpecException e) {
- // Actually this exception shouldn't be thrown
- throw new InvalidKeyException("ATTENTION: InvalidKeySpecException: " + e);
- }
- }
-
- if (key instanceof DSAPublicKey) {
-
- DSAPublicKey publicKey = (DSAPublicKey) key;
- DSAParams params = publicKey.getParams();
-
- try {
- return engineGeneratePublic(new DSAPublicKeySpec(publicKey
- .getY(), params.getP(), params.getQ(), params
- .getG()));
- } catch (InvalidKeySpecException e) {
- // Actually this exception shouldn't be thrown
- throw new InvalidKeyException("ATTENTION: InvalidKeySpecException: " + e);
- }
- }
- }
- throw new InvalidKeyException("'key' is neither DSAPublicKey nor DSAPrivateKey");
- }
-
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAPrivateKeyImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAPrivateKeyImpl.java
deleted file mode 100644
index c0fc766..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAPrivateKeyImpl.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
- /*
- * TODO
- * 1. The class extends the PrivateKeyImpl class in "org.apache.harmony.security" package.
- *
- * 2. See a compatibility with RI comments
- * in the below "DSAPrivateKeyImpl(PKCS8EncodedKeySpec keySpec)" constructor.
- */
-
-
-package org.apache.harmony.security.provider.crypto;
-
-import java.io.IOException;
-import java.io.NotActiveException;
-import java.math.BigInteger;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.spec.DSAParameterSpec;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import org.apache.harmony.security.PrivateKeyImpl;
-import org.apache.harmony.security.asn1.ASN1Integer;
-import org.apache.harmony.security.pkcs8.PrivateKeyInfo;
-import org.apache.harmony.security.utils.AlgNameMapper;
-import org.apache.harmony.security.x509.AlgorithmIdentifier;
-
-/**
- * The class provides DSAPrivateKey functionality by extending a class implementing PrivateKey
- * and implementing methods defined in both interfaces, DSAKey and DSAPrivateKey
- */
-public class DSAPrivateKeyImpl extends PrivateKeyImpl implements DSAPrivateKey {
-
- /**
- * @serial
- */
- private static final long serialVersionUID = -4716227614104950081L;
-
- private BigInteger x, g, p, q;
-
- private transient DSAParams params;
-
- /**
- * Creates object from DSAPrivateKeySpec.
- *
- * @param keySpec - a DSAPrivateKeySpec object
- */
- public DSAPrivateKeyImpl(DSAPrivateKeySpec keySpec) {
-
- super("DSA");
-
- PrivateKeyInfo pki;
-
- g = keySpec.getG();
- p = keySpec.getP();
- q = keySpec.getQ();
-
- ThreeIntegerSequence threeInts = new ThreeIntegerSequence(p
- .toByteArray(), q.toByteArray(), g.toByteArray());
-
- AlgorithmIdentifier ai = new AlgorithmIdentifier(AlgNameMapper
- .map2OID("DSA"),
- threeInts.getEncoded());
- x = keySpec.getX();
-
- pki = new PrivateKeyInfo(0, ai, ASN1Integer.getInstance().encode(
- x.toByteArray()), null);
-
- setEncoding(pki.getEncoded());
-
- params = new DSAParameterSpec(p, q, g);
- }
-
- /**
- * Creates object from PKCS8EncodedKeySpec.
- *
- * @param keySpec - a XPKCS8EncodedKeySpec object
- *
- * @throws InvalidKeySpecException - if key data cannot be obtain from encoded format
- */
- public DSAPrivateKeyImpl(PKCS8EncodedKeySpec keySpec)
- throws InvalidKeySpecException {
-
- super("DSA");
-
- AlgorithmIdentifier ai;
- ThreeIntegerSequence threeInts = null;
-
- String alg, algName;
-
- byte[] encoding = keySpec.getEncoded();
-
- PrivateKeyInfo privateKeyInfo = null;
-
- try {
- privateKeyInfo = (PrivateKeyInfo) PrivateKeyInfo.ASN1
- .decode(encoding);
- } catch (IOException e) {
- throw new InvalidKeySpecException("Failed to decode keySpec encoding: " + e);
- }
-
- try {
- x = new BigInteger((byte[]) ASN1Integer.getInstance().decode(
- privateKeyInfo.getPrivateKey()));
- } catch (IOException e) {
- throw new InvalidKeySpecException("Failed to decode parameters: " + e);
- }
-
- ai = privateKeyInfo.getAlgorithmIdentifier();
- try {
- threeInts = (ThreeIntegerSequence) ThreeIntegerSequence.ASN1
- .decode(ai.getParameters());
- } catch (IOException e) {
- throw new InvalidKeySpecException("Failed to decode parameters: " + e);
- }
- p = new BigInteger(threeInts.p);
- q = new BigInteger(threeInts.q);
- g = new BigInteger(threeInts.g);
- params = new DSAParameterSpec(p, q, g);
- setEncoding(encoding);
-
- /*
- * the following code implements RI behavior
- */
- alg = ai.getAlgorithm();
- algName = AlgNameMapper.map2AlgName(alg);
- setAlgorithm(algName == null ? alg : algName);
- }
-
- public BigInteger getX() {
- return x;
- }
-
- public DSAParams getParams() {
- return params;
- }
-
- private void readObject(java.io.ObjectInputStream in) throws NotActiveException, IOException, ClassNotFoundException {
- in.defaultReadObject();
- params = new DSAParameterSpec(p, q, g);
- }
-
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAPublicKeyImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAPublicKeyImpl.java
deleted file mode 100644
index 6b35970..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/DSAPublicKeyImpl.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
- /*
- * TODO
- * 1. The class extends the PublicKeyImpl class in "org.apache.harmony.security" package.
- *
- * 2. The class uses methods in the auxiliary non-public "ThreeIntegerSequence" class
- * defined along with the "DSAPrivateKeyImpl" class.
- *
- * 3. See a compatibility with RI comments
- * in the below "DSAPublicKeyImpl(X509EncodedKeySpec keySpec)" constructor.
- */
-
-package org.apache.harmony.security.provider.crypto;
-
-import java.io.IOException;
-import java.io.NotActiveException;
-import java.math.BigInteger;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAParameterSpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-import org.apache.harmony.security.PublicKeyImpl;
-import org.apache.harmony.security.asn1.ASN1Integer;
-import org.apache.harmony.security.utils.AlgNameMapper;
-import org.apache.harmony.security.x509.AlgorithmIdentifier;
-import org.apache.harmony.security.x509.SubjectPublicKeyInfo;
-
-/**
- * The class provides DSAPublicKey functionality by extending a class implementing PublicKey
- * and implementing methods defined in both interfaces, DSAKey and DSAPublicKey
- */
-public class DSAPublicKeyImpl extends PublicKeyImpl implements DSAPublicKey {
-
- /**
- * @serial
- */
- private static final long serialVersionUID = -2279672131310978336L;
-
- private BigInteger y, g, p, q;
-
- private transient DSAParams params;
-
- /**
- * Creates object from DSAPublicKeySpec.
- *
- * @param keySpec - a DSAPublicKeySpec object
- */
- public DSAPublicKeyImpl(DSAPublicKeySpec keySpec) {
-
- super("DSA");
-
- SubjectPublicKeyInfo spki;
-
- p = keySpec.getP();
- q = keySpec.getQ();
- g = keySpec.getG();
-
- ThreeIntegerSequence threeInts = new ThreeIntegerSequence(p
- .toByteArray(), q.toByteArray(), g.toByteArray());
-
- AlgorithmIdentifier ai = new AlgorithmIdentifier(AlgNameMapper
- .map2OID("DSA"),
- threeInts.getEncoded());
-
- y = keySpec.getY();
-
- spki = new SubjectPublicKeyInfo(ai, ASN1Integer.getInstance().encode(
- y.toByteArray()));
- setEncoding(spki.getEncoded());
-
- params = (DSAParams) (new DSAParameterSpec(p, q, g));
- }
-
- /**
- * Creates object from X509EncodedKeySpec.
- *
- * @param keySpec - a X509EncodedKeySpec object
- *
- * @throws InvalidKeySpecException - if key data cannot be obtain from encoded format
- */
- public DSAPublicKeyImpl(X509EncodedKeySpec keySpec)
- throws InvalidKeySpecException {
-
- super("DSA");
-
- AlgorithmIdentifier ai;
- ThreeIntegerSequence threeInts = null;
-
- SubjectPublicKeyInfo subjectPublicKeyInfo = null;
-
- byte[] encoding = keySpec.getEncoded();
-
- String alg, algName;
-
- try {
- subjectPublicKeyInfo = (SubjectPublicKeyInfo) SubjectPublicKeyInfo.ASN1
- .decode(encoding);
- } catch (IOException e) {
- throw new InvalidKeySpecException("Failed to decode keySpec encoding: " + e);
- }
-
- try {
- y = new BigInteger((byte[]) ASN1Integer.getInstance().decode(
- subjectPublicKeyInfo.getSubjectPublicKey()));
- } catch (IOException e) {
- throw new InvalidKeySpecException("Failed to decode parameters: " + e);
- }
-
- ai = subjectPublicKeyInfo.getAlgorithmIdentifier();
-
- try {
- threeInts = (ThreeIntegerSequence) ThreeIntegerSequence.ASN1
- .decode(ai.getParameters());
- } catch (IOException e) {
- throw new InvalidKeySpecException("Failed to decode parameters: " + e);
- }
- p = new BigInteger(threeInts.p);
- q = new BigInteger(threeInts.q);
- g = new BigInteger(threeInts.g);
- params = (DSAParams) (new DSAParameterSpec(p, q, g));
-
- setEncoding(encoding);
-
- /*
- * the following code implements RI behavior
- */
- alg = ai.getAlgorithm();
- algName = AlgNameMapper.map2AlgName(alg);
- setAlgorithm(algName == null ? alg : algName);
- }
-
- /**
- * @return
- * a value of a public key (y).
- */
- public BigInteger getY() {
- return y;
- }
-
- /**
- * @return
- * DSA key parameters (p, q, g).
- */
- public DSAParams getParams() {
- return params;
- }
-
- private void readObject(java.io.ObjectInputStream in) throws NotActiveException, IOException, ClassNotFoundException {
- in.defaultReadObject();
- params = new DSAParameterSpec(p, q, g);
- }
-
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1_MessageDigestImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1_MessageDigestImpl.java
deleted file mode 100644
index 3f41f18..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1_MessageDigestImpl.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.harmony.security.provider.crypto;
-
-import java.security.DigestException;
-import java.security.MessageDigestSpi;
-import java.util.Arrays;
-
-import static org.apache.harmony.security.provider.crypto.SHA1Constants.*;
-
-/**
- * This class extends the MessageDigestSpi class implementing all its abstract methods;
- * it overrides the "Object clone()" and "int engineGetDigestLength()" methods. <BR>
- * The class implements the Cloneable interface.
- */
-public class SHA1_MessageDigestImpl extends MessageDigestSpi implements Cloneable {
- private int[] buffer; // buffer has the following structure:
- // - 0-16 - frame for accumulating a message
- // - 17-79 - for SHA1Impl methods
- // - 80 - unused
- // - 81 - to store length of the message
- // - 82-86 - frame for current message digest
-
- private byte[] oneByte; // one byte buffer needed to use in engineUpdate(byte)
- // having buffer as private field is just optimization
-
- private long messageLength; // total length of bytes supplied by user
-
-
- /**
- * The constructor creates needed buffers and sets the engine at initial state
- */
- public SHA1_MessageDigestImpl() {
-
- // BYTES_OFFSET +6 is minimal length required by methods in SHA1Impl
- buffer = new int[BYTES_OFFSET +6];
-
- oneByte = new byte[1];
-
- engineReset();
- }
-
-
- /**
- * The method performs final actions and invokes the "computeHash(int[])" method.
- * In case if there is no enough words in current frame
- * after processing its data, extra frame is prepared and
- * the "computeHash(int[])" method is invoked second time. <BR>
- *
- * After processing, the method resets engine's state
- *
- * @param
- * digest - byte array
- * @param
- * offset - offset in digest
- */
- private void processDigest(byte[] digest, int offset) {
-
- int i, j; // implementation variables
- int lastWord; //
-
- long nBits = messageLength <<3 ; // length has to be calculated before padding
-
- engineUpdate( (byte) 0x80 ); // beginning byte in padding
-
- i = 0; // i contains number of beginning word for following loop
-
- lastWord = (buffer[BYTES_OFFSET] + 3)>>2 ; // computing of # of full words by shifting
- // # of bytes
-
- // possible cases:
- //
- // - buffer[BYTES_OFFSET] == 0 - buffer frame is empty,
- // padding byte was 64th in previous frame
- // current frame should contain only message's length
- //
- // - lastWord < 14 - two last, these are 14 & 15, words in 16 word frame are free;
- // no extra frame needed
- // - lastWord = 14 - only one last, namely 15-th, word in frame doesn't contain bytes;
- // extra frame is needed
- // - lastWord > 14 - last word in frame is not full;
- // extra frame is needed
-
- if ( buffer[BYTES_OFFSET] != 0 ) {
-
- if ( lastWord < 15 ) {
- i = lastWord;
- } else {
- if ( lastWord == 15 ) {
- buffer[15] = 0; // last word in frame is set to "0"
- }
- SHA1Impl.computeHash(buffer);
- i = 0;
- }
- }
- Arrays.fill(buffer, i, 14, 0);
-
- buffer[14] = (int)( nBits >>>32 );
- buffer[15] = (int)( nBits & 0xFFFFFFFF );
- SHA1Impl.computeHash(buffer);
-
- // converting 5-word frame into 20 bytes
- j = offset;
- for ( i = HASH_OFFSET; i < HASH_OFFSET +5; i++ ) {
- int k = buffer[i];
- digest[j ] = (byte) ( k >>>24 ); // getting first byte from left
- digest[j+1] = (byte) ( k >>>16 ); // getting second byte from left
- digest[j+2] = (byte) ( k >>> 8 ); // getting third byte from left
- digest[j+3] = (byte) ( k ); // getting fourth byte from left
- j += 4;
- }
-
- engineReset();
- }
-
- // methods specified in java.security.MessageDigestSpi
-
- /**
- * Returns a "deep" copy of this SHA1MDImpl object. <BR>
- *
- * The method overrides "clone()" in class Object. <BR>
- *
- * @return
- * a clone of this object
- */
- public Object clone() throws CloneNotSupportedException {
- SHA1_MessageDigestImpl cloneObj = (SHA1_MessageDigestImpl) super.clone();
- cloneObj.buffer = buffer.clone();
- cloneObj.oneByte = oneByte.clone();
- return cloneObj;
- }
-
-
- /**
- * Computes a message digest value. <BR>
- *
- * The method resets the engine. <BR>
- *
- * The method overrides "engineDigest()" in class MessageDigestSpi. <BR>
- *
- * @return
- * byte array containing message digest value
- */
- protected byte[] engineDigest() {
- byte[] hash = new byte[DIGEST_LENGTH];
- processDigest(hash, 0);
- return hash;
- }
-
-
- /**
- * Computes message digest value.
- * Upon return, the value is stored in "buf" buffer beginning "offset" byte. <BR>
- *
- * The method resets the engine. <BR>
- *
- * The method overrides "engineDigest(byte[],int,int) in class MessageDigestSpi.
- *
- * @param
- * buf byte array to store a message digest returned
- * @param
- * offset a position in the array for first byte of the message digest
- * @param
- * len number of bytes within buffer allotted for the message digest;
- * as this implementation doesn't provide partial digests,
- * len should be >= 20, DigestException is thrown otherwise
- * @return
- * the length of the message digest stored in the "buf" buffer;
- * in this implementation the length=20
- *
- * @throws IllegalArgumentException
- * if null is passed to the "buf" argument <BR>
- * if offset + len > buf.length <BR>
- * if offset > buf.length or len > buf.length
- *
- * @throws DigestException
- * if len < 20
- *
- * @throws ArrayIndexOutOfBoundsException
- * if offset < 0
- */
- protected int engineDigest(byte[] buf, int offset, int len) throws DigestException {
- if (buf == null) {
- throw new IllegalArgumentException("buf == null");
- }
- if (offset > buf.length || len > buf.length || (len + offset) > buf.length) {
- throw new IllegalArgumentException();
- }
- if (len < DIGEST_LENGTH) {
- throw new DigestException("len < DIGEST_LENGTH");
- }
- if (offset < 0) {
- throw new ArrayIndexOutOfBoundsException(offset);
- }
-
- processDigest(buf, offset);
-
- return DIGEST_LENGTH;
- }
-
-
- /**
- * Returns a message digest length. <BR>
- *
- * The method overrides "engineGetDigestLength()" in class MessageDigestSpi. <BR>
- *
- * @return
- * total length of current message digest as an int value
- */
- protected int engineGetDigestLength() {
- return DIGEST_LENGTH;
- }
-
-
- /**
- * Resets the engine. <BR>
- *
- * The method overrides "engineReset()" in class MessageDigestSpi. <BR>
- */
- protected void engineReset() {
-
- messageLength = 0;
-
- buffer[BYTES_OFFSET] = 0;
- buffer[HASH_OFFSET ] = H0;
- buffer[HASH_OFFSET +1] = H1;
- buffer[HASH_OFFSET +2] = H2;
- buffer[HASH_OFFSET +3] = H3;
- buffer[HASH_OFFSET +4] = H4;
- }
-
-
- /**
- * Supplements a byte to current message. <BR>
- *
- * The method overrides "engineUpdate(byte)" in class MessageDigestSpi. <BR>
- *
- * @param
- * input byte to add to current message
- */
- protected void engineUpdate(byte input) {
-
- oneByte[0] = input;
- SHA1Impl.updateHash( buffer, oneByte, 0, 0 );
- messageLength++;
- }
-
-
- /**
- * Updates current message. <BR>
- *
- * The method overrides "engineUpdate(byte[],int,int)" in class MessageDigestSpi. <BR>
- *
- * The method silently returns if "len" <= 0.
- *
- * @param
- * input a byte array
- * @param
- * offset a number of first byte in the "input" array to use for updating
- * @param
- * len a number of bytes to use
- *
- * @throws NullPointerException
- * if null is passed to the "buf" argument
- *
- * @throws IllegalArgumentException
- * if offset > buf.length or len > buf.length or
- * (len + offset) > buf.length
- * @throws ArrayIndexOutOfBoundsException
- * offset < 0
- */
- protected void engineUpdate(byte[] input, int offset, int len) {
- if (input == null) {
- throw new IllegalArgumentException("input == null");
- }
- if (len <= 0) {
- return;
- }
- if (offset < 0) {
- throw new ArrayIndexOutOfBoundsException(offset);
- }
- if (offset > input.length || len > input.length || (len + offset) > input.length) {
- throw new IllegalArgumentException();
- }
-
- SHA1Impl.updateHash(buffer, input, offset, offset + len -1 );
- messageLength += len;
- }
-
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java
deleted file mode 100644
index 2958e00..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.harmony.security.provider.crypto;
-
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.InvalidParameterException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.interfaces.DSAKey;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-
-public class SHA1withDSA_SignatureImpl extends Signature {
-
- private MessageDigest msgDigest;
-
- private DSAKey dsaKey;
-
- /**
- * The solo constructor.
- */
- public SHA1withDSA_SignatureImpl() throws NoSuchAlgorithmException {
-
- super("SHA1withDSA");
-
- msgDigest = MessageDigest.getInstance("SHA1");
- }
-
- /**
- * Deprecated method.
- *
- * @return
- * null
- */
- protected Object engineGetParameter(String param)
- throws InvalidParameterException {
- if (param == null) {
- throw new NullPointerException("param == null");
- }
- return null;
- }
-
- /**
- * Initializes this signature object with PrivateKey object
- * passed as argument to the method.
- *
- * @params
- * privateKey DSAPrivateKey object
- * @throws
- * InvalidKeyException if privateKey is not DSAPrivateKey object
- */
- protected void engineInitSign(PrivateKey privateKey)
- throws InvalidKeyException {
-
- DSAParams params;
-
- // parameters and private key
- BigInteger p, q, x;
-
- int n;
-
- if (privateKey == null || !(privateKey instanceof DSAPrivateKey)) {
- throw new InvalidKeyException();
- }
-
- params = ((DSAPrivateKey) privateKey).getParams();
- p = params.getP();
- q = params.getQ();
- x = ((DSAPrivateKey) privateKey).getX();
-
- // checks described in DSA standard
- n = p.bitLength();
- if (p.compareTo(BigInteger.valueOf(1)) != 1 || n < 512 || n > 1024 || (n & 077) != 0) {
- throw new InvalidKeyException("bad p");
- }
- if (q.signum() != 1 && q.bitLength() != 160) {
- throw new InvalidKeyException("bad q");
- }
- if (x.signum() != 1 || x.compareTo(q) != -1) {
- throw new InvalidKeyException("x <= 0 || x >= q");
- }
-
- dsaKey = (DSAKey) privateKey;
-
- msgDigest.reset();
- }
-
- /**
- * Initializes this signature object with PublicKey object
- * passed as argument to the method.
- *
- * @params
- * publicKey DSAPublicKey object
- * @throws
- * InvalidKeyException if publicKey is not DSAPublicKey object
- */
- protected void engineInitVerify(PublicKey publicKey)
- throws InvalidKeyException {
-
- // parameters and public key
- BigInteger p, q, y;
-
- int n1;
-
- if (publicKey == null || !(publicKey instanceof DSAPublicKey)) {
- throw new InvalidKeyException("publicKey is not an instance of DSAPublicKey");
- }
-
- DSAParams params = ((DSAPublicKey) publicKey).getParams();
- p = params.getP();
- q = params.getQ();
- y = ((DSAPublicKey) publicKey).getY();
-
- // checks described in DSA standard
- n1 = p.bitLength();
- if (p.compareTo(BigInteger.valueOf(1)) != 1 || n1 < 512 || n1 > 1024 || (n1 & 077) != 0) {
- throw new InvalidKeyException("bad p");
- }
- if (q.signum() != 1 || q.bitLength() != 160) {
- throw new InvalidKeyException("bad q");
- }
- if (y.signum() != 1) {
- throw new InvalidKeyException("y <= 0");
- }
-
- dsaKey = (DSAKey) publicKey;
-
- msgDigest.reset();
- }
-
- /*
- * Deprecated method.
- *
- * @throws
- * InvalidParameterException
- */
- protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
- if (param == null) {
- throw new NullPointerException("param == null");
- }
- throw new InvalidParameterException("invalid parameter for this engine");
- }
-
- /**
- * Returns signature bytes as byte array containing
- * ASN1 representation for two BigInteger objects
- * which is SEQUENCE of two INTEGERS.
- * Length of sequence varies from less than 46 to 48.
- *
- * Resets object to the state it was in
- * when previous call to either "initSign" method was called.
- *
- * @return
- * byte array containing signature in ASN1 representation
- * @throws
- * SignatureException if object's state is not SIGN or
- * signature algorithm cannot process data
- */
-
- protected byte[] engineSign() throws SignatureException {
-
- // names of below BigIntegers are the same as they are defined in DSA standard
- BigInteger r = null;
- BigInteger s = null;
- BigInteger k = null;
-
- // parameters and private key
- BigInteger p, q, g, x;
-
- // BigInteger for message digest
- BigInteger digestBI;
-
- // various byte array being used in computing signature
- byte[] randomBytes;
- byte[] rBytes;
- byte[] sBytes;
- byte[] signature;
-
- int n, n1, n2;
-
- DSAParams params;
-
- if (appRandom == null) {
- appRandom = new SecureRandom();
- }
-
- params = dsaKey.getParams();
- p = params.getP();
- q = params.getQ();
- g = params.getG();
- x = ((DSAPrivateKey) dsaKey).getX();
-
- // forming signature according algorithm described in chapter 5 of DSA standard
-
- digestBI = new BigInteger(1, msgDigest.digest());
-
- randomBytes = new byte[20];
-
- for (;;) {
-
- appRandom.nextBytes(randomBytes);
-
- k = new BigInteger(1, randomBytes);
- if (k.compareTo(q) != -1) {
- continue;
- }
- r = g.modPow(k, p).mod(q);
- if (r.signum() == 0) {
- continue;
- }
-
- s = k.modInverse(q).multiply(digestBI.add(x.multiply(r)).mod(q))
- .mod(q);
-
- if (s.signum() != 0) {
- break;
- }
- }
-
- // forming signature's ASN1 representation which is SEQUENCE of two INTEGERs
- //
- rBytes = r.toByteArray();
- n1 = rBytes.length;
- if ((rBytes[0] & 0x80) != 0) {
- n1++;
- }
- sBytes = s.toByteArray();
- n2 = sBytes.length;
- if ((sBytes[0] & 0x80) != 0) {
- n2++;
- }
-
- signature = new byte[6 + n1 + n2]; // 48 is max. possible length of signature
- signature[0] = (byte) 0x30; // ASN1 SEQUENCE tag
- signature[1] = (byte) (4 + n1 + n2); // total length of two INTEGERs
- signature[2] = (byte) 0x02; // ASN1 INTEGER tag
- signature[3] = (byte) n1; // length of r
- signature[4 + n1] = (byte) 0x02; // ASN1 INTEGER tag
- signature[5 + n1] = (byte) n2; // length of s
-
- if (n1 == rBytes.length) {
- n = 4;
- } else {
- n = 5;
- }
- System.arraycopy(rBytes, 0, signature, n, rBytes.length);
-
- if (n2 == sBytes.length) {
- n = 6 + n1;
- } else {
- n = 7 + n1;
- }
- System.arraycopy(sBytes, 0, signature, n, sBytes.length);
-
- return signature;
- }
-
- /**
- * Updates data to sign or to verify.
- *
- * @params
- * b byte to update
- * @throws
- * SignatureException if object was not initialized for signing or verifying
- */
- protected void engineUpdate(byte b) throws SignatureException {
-
- msgDigest.update(b);
- }
-
- /**
- * Updates data to sign or to verify.
- *
- * @params
- * b byte array containing bytes to update
- * @params
- * off offset in byte array to start from
- * @params
- * len number of bytes to use for updating
- * @throws
- * SignatureException if object was not initialized for signing or verifying
- */
- protected void engineUpdate(byte[] b, int off, int len)
- throws SignatureException {
-
- msgDigest.update(b, off, len);
- }
-
- private boolean checkSignature(byte[] sigBytes, int offset, int length)
- throws SignatureException {
-
- // names of below BigIntegers are the same as they are defined in DSA standard
- BigInteger r, s, w;
- BigInteger u1, u2, v;
-
- // parameters and public key
- BigInteger p, q, g, y;
-
- DSAParams params;
-
- int n1, n2;
-
- byte[] bytes;
- byte[] digest;
-
- // checking up on signature's ASN1
- try {
- byte dummy;
- n1 = sigBytes[offset + 3];
- n2 = sigBytes[offset + n1 + 5];
-
- if (sigBytes[offset + 0] != 0x30 || sigBytes[offset + 2] != 2
- || sigBytes[offset + n1 + 4] != 2
- || sigBytes[offset + 1] != (n1 + n2 + 4) || n1 > 21
- || n2 > 21
- || (length != 0 && (sigBytes[offset + 1] + 2) > length)) {
- throw new SignatureException("signature bytes have invalid encoding");
- }
-
- dummy = sigBytes[5 + n1 + n2]; // to check length of sigBytes
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new SignatureException("bad argument: byte[] is too small");
- }
-
- digest = msgDigest.digest();
-
- bytes = new byte[n1];
- System.arraycopy(sigBytes, offset + 4, bytes, 0, n1);
- r = new BigInteger(bytes);
-
- bytes = new byte[n2];
- System.arraycopy(sigBytes, offset + 6 + n1, bytes, 0, n2);
- s = new BigInteger(bytes);
-
- params = dsaKey.getParams();
- p = params.getP();
- q = params.getQ();
- g = params.getG();
- y = ((DSAPublicKey) dsaKey).getY();
-
- // forming signature according algorithm described in chapter 6 of DSA standard
-
- if (r.signum() != 1 || r.compareTo(q) != -1 || s.signum() != 1
- || s.compareTo(q) != -1) {
- return false;
- }
-
- w = s.modInverse(q);
-
- u1 = (new BigInteger(1, digest)).multiply(w).mod(q);
- u2 = r.multiply(w).mod(q);
-
- v = g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).mod(q);
-
- if (v.compareTo(r) != 0) {
- return false;
- }
- return true;
- }
-
- /**
- * Verifies the signature bytes.
- *
- * @params
- * sigBytes byte array with signature bytes to verify.
- * @return
- * true if signature bytes were verified, false otherwise
- * @throws
- * SignatureException if object's state is not VERIFY or
- * signature format is not ASN1 representation or
- * signature algorithm cannot process data
- */
- protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
- if (sigBytes == null) {
- throw new NullPointerException("sigBytes == null");
- }
-
- return checkSignature(sigBytes, 0, 0);
- }
-
- /**
- * Verifies the signature bytes.
- *
- * @params
- * sigBytes byte array with signature bytes to verify.
- * @params
- * offset index in sigBytes to start from
- * @params
- * length number of bytes allotted for signature
- * @return
- * true if signature bytes were verified, false otherwise
- * @throws
- * SignatureException if object's state is not VERIFY or
- * signature format is not ASN1 representation or
- * signature algorithm cannot process data
- */
- protected boolean engineVerify(byte[] sigBytes, int offset, int length)
- throws SignatureException {
- return checkSignature(sigBytes, offset, length);
- }
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/ThreeIntegerSequence.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/ThreeIntegerSequence.java
deleted file mode 100644
index 4f4232a..0000000
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/ThreeIntegerSequence.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.harmony.security.provider.crypto;
-
-import org.apache.harmony.security.asn1.ASN1Integer;
-import org.apache.harmony.security.asn1.ASN1Sequence;
-import org.apache.harmony.security.asn1.ASN1Type;
-import org.apache.harmony.security.asn1.BerInputStream;
-
-
-/**
- * The auxiliary class providing means to process ASN1Sequence of three Integers.
- * Such sequences are parts of ASN1 encoded formats for DSA private and public keys.
- */
-class ThreeIntegerSequence {
-
- byte[] p, q, g;
-
- private byte[] encoding;
-
- ThreeIntegerSequence(byte[] p, byte[] q, byte[] g) {
-
- this.p = p;
- this.q = q;
- this.g = g;
- encoding = null;
- }
-
- public byte[] getEncoded() {
- if (encoding == null) {
- encoding = ASN1.encode(this);
- }
- return encoding;
- }
-
- public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] {
- ASN1Integer.getInstance(), ASN1Integer.getInstance(),
- ASN1Integer.getInstance() }) {
-
- protected Object getDecodedObject(BerInputStream in) {
-
- Object[] values = (Object[]) in.content;
-
- return new ThreeIntegerSequence((byte[]) values[0],
- (byte[]) values[1], (byte[]) values[2]);
- }
-
- protected void getValues(Object object, Object[] values) {
-
- ThreeIntegerSequence mySeq = (ThreeIntegerSequence) object;
-
- values[0] = mySeq.p;
- values[1] = mySeq.q;
- values[2] = mySeq.g;
- }
- };
-}
diff --git a/luni/src/main/java/org/apache/harmony/xml/ExpatParser.java b/luni/src/main/java/org/apache/harmony/xml/ExpatParser.java
index db6f4ef..fa6308e 100644
--- a/luni/src/main/java/org/apache/harmony/xml/ExpatParser.java
+++ b/luni/src/main/java/org/apache/harmony/xml/ExpatParser.java
@@ -48,7 +48,7 @@
private boolean inStartElement = false;
private int attributeCount = -1;
- private int attributePointer = 0;
+ private long attributePointer = 0;
private final Locator locator = new ExpatLocator();
@@ -129,7 +129,7 @@
* @param attributeCount number of attributes
*/
/*package*/ void startElement(String uri, String localName, String qName,
- int attributePointer, int attributeCount) throws SAXException {
+ long attributePointer, int attributeCount) throws SAXException {
ContentHandler contentHandler = xmlReader.contentHandler;
if (contentHandler == null) {
return;
@@ -772,7 +772,7 @@
@Override
void startElement(String uri, String localName, String qName,
- int attributePointer, int attributeCount) throws SAXException {
+ long attributePointer, int attributeCount) throws SAXException {
/*
* Skip topmost element generated by our workaround in
* {@link #handleExternalEntity}.
diff --git a/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp b/luni/src/main/native/AsynchronousCloseMonitor.cpp
similarity index 72%
rename from luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
rename to luni/src/main/native/AsynchronousCloseMonitor.cpp
index 9617e9d..31cde00 100644
--- a/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
+++ b/luni/src/main/native/AsynchronousCloseMonitor.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#define LOG_TAG "AsynchronousSocketCloseMonitor"
+#define LOG_TAG "AsynchronousCloseMonitor"
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
#include "cutils/log.h"
#include <errno.h>
@@ -27,27 +27,28 @@
* We use an intrusive doubly-linked list to keep track of blocked threads.
* This gives us O(1) insertion and removal, and means we don't need to do any allocation.
* (The objects themselves are stack-allocated.)
- * Waking potentially-blocked threads when a socket is closed is O(n) in the total number of
- * blocked threads (not the number of threads actually blocked on the socket in question).
- * For now at least, this seems like a good compromise for Android.
+ * Waking potentially-blocked threads when a file descriptor is closed is O(n) in the total number
+ * of blocked threads (not the number of threads actually blocked on the file descriptor in
+ * question). For now at least, this seems like a good compromise for Android.
*/
static pthread_mutex_t blockedThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
-static AsynchronousSocketCloseMonitor* blockedThreadList = NULL;
+static AsynchronousCloseMonitor* blockedThreadList = NULL;
/**
- * The specific signal chosen here is arbitrary.
+ * The specific signal chosen here is arbitrary, but bionic needs to know so that SIGRTMIN
+ * starts at a higher value.
*/
#if defined(__APPLE__)
static const int BLOCKED_THREAD_SIGNAL = SIGUSR2;
#else
-static const int BLOCKED_THREAD_SIGNAL = SIGRTMIN + 2;
+static const int BLOCKED_THREAD_SIGNAL = __SIGRTMIN + 2;
#endif
static void blockedThreadSignalHandler(int /*signal*/) {
// Do nothing. We only sent this signal for its side-effect of interrupting syscalls.
}
-void AsynchronousSocketCloseMonitor::init() {
+void AsynchronousCloseMonitor::init() {
// Ensure that the signal we send interrupts system calls but doesn't kill threads.
// Using sigaction(2) lets us ensure that the SA_RESTART flag is not set.
// (The whole reason we're sending this signal is to unblock system calls!)
@@ -61,21 +62,27 @@
}
}
-void AsynchronousSocketCloseMonitor::signalBlockedThreads(int fd) {
+void AsynchronousCloseMonitor::signalBlockedThreads(int fd) {
ScopedPthreadMutexLock lock(&blockedThreadListMutex);
- for (AsynchronousSocketCloseMonitor* it = blockedThreadList; it != NULL; it = it->mNext) {
+ for (AsynchronousCloseMonitor* it = blockedThreadList; it != NULL; it = it->mNext) {
if (it->mFd == fd) {
+ it->mSignaled = true;
pthread_kill(it->mThread, BLOCKED_THREAD_SIGNAL);
// Keep going, because there may be more than one thread...
}
}
}
-AsynchronousSocketCloseMonitor::AsynchronousSocketCloseMonitor(int fd) {
+bool AsynchronousCloseMonitor::wasSignaled() const {
+ return mSignaled;
+}
+
+AsynchronousCloseMonitor::AsynchronousCloseMonitor(int fd) {
ScopedPthreadMutexLock lock(&blockedThreadListMutex);
// Who are we, and what are we waiting for?
mThread = pthread_self();
mFd = fd;
+ mSignaled = false;
// Insert ourselves at the head of the intrusive doubly-linked list...
mPrev = NULL;
mNext = blockedThreadList;
@@ -85,7 +92,7 @@
blockedThreadList = this;
}
-AsynchronousSocketCloseMonitor::~AsynchronousSocketCloseMonitor() {
+AsynchronousCloseMonitor::~AsynchronousCloseMonitor() {
ScopedPthreadMutexLock lock(&blockedThreadListMutex);
// Unlink ourselves from the intrusive doubly-linked list...
if (mNext != NULL) {
diff --git a/luni/src/main/native/AsynchronousCloseMonitor.h b/luni/src/main/native/AsynchronousCloseMonitor.h
new file mode 100644
index 0000000..eefbbdf
--- /dev/null
+++ b/luni/src/main/native/AsynchronousCloseMonitor.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef ASYNCHRONOUS_CLOSE_MONITOR_H_included
+#define ASYNCHRONOUS_CLOSE_MONITOR_H_included
+
+#include "ScopedPthreadMutexLock.h"
+#include <pthread.h>
+
+/**
+ * AsynchronousCloseMonitor helps implement Java's asynchronous close semantics.
+ *
+ * AsynchronousCloseMonitor::init must be called before anything else.
+ *
+ * Every blocking I/O operation must be surrounded by an AsynchronousCloseMonitor
+ * instance. For example:
+ *
+ * {
+ * AsynchronousCloseMonitor monitor(fd);
+ * byteCount = ::read(fd, buf, sizeof(buf));
+ * }
+ *
+ * To interrupt all threads currently blocked on file descriptor 'fd', call signalBlockedThreads:
+ *
+ * AsynchronousCloseMonitor::signalBlockedThreads(fd);
+ *
+ * To test to see if the interruption was due to the signalBlockedThreads call:
+ *
+ * monitor.wasSignaled();
+ */
+class AsynchronousCloseMonitor {
+public:
+ AsynchronousCloseMonitor(int fd);
+ ~AsynchronousCloseMonitor();
+ bool wasSignaled() const;
+
+ static void init();
+
+ static void signalBlockedThreads(int fd);
+
+private:
+ AsynchronousCloseMonitor* mPrev;
+ AsynchronousCloseMonitor* mNext;
+ pthread_t mThread;
+ int mFd;
+ bool mSignaled;
+
+ // Disallow copy and assignment.
+ AsynchronousCloseMonitor(const AsynchronousCloseMonitor&);
+ void operator=(const AsynchronousCloseMonitor&);
+};
+
+#endif // ASYNCHRONOUS_CLOSE_MONITOR_H_included
diff --git a/luni/src/main/native/AsynchronousSocketCloseMonitor.h b/luni/src/main/native/AsynchronousSocketCloseMonitor.h
deleted file mode 100644
index 3370e22..0000000
--- a/luni/src/main/native/AsynchronousSocketCloseMonitor.h
+++ /dev/null
@@ -1,60 +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.
- */
-
-#ifndef ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
-#define ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
-
-#include "ScopedPthreadMutexLock.h"
-#include <pthread.h>
-
-/**
- * AsynchronousSocketCloseMonitor helps implement Java's asynchronous Socket.close semantics.
- *
- * AsynchronousSocketCloseMonitor::init must be called before anything else.
- *
- * Every blocking network I/O operation must be surrounded by an AsynchronousSocketCloseMonitor
- * instance. For example:
- *
- * {
- * AsynchronousSocketCloseMonitor monitor(fd);
- * byteCount = ::read(fd, buf, sizeof(buf));
- * }
- *
- * To interrupt all threads currently blocked on file descriptor 'fd', call signalBlockedThreads:
- *
- * AsynchronousSocketCloseMonitor::signalBlockedThreads(fd);
- */
-class AsynchronousSocketCloseMonitor {
-public:
- AsynchronousSocketCloseMonitor(int fd);
- ~AsynchronousSocketCloseMonitor();
-
- static void init();
-
- static void signalBlockedThreads(int fd);
-
-private:
- AsynchronousSocketCloseMonitor* mPrev;
- AsynchronousSocketCloseMonitor* mNext;
- pthread_t mThread;
- int mFd;
-
- // Disallow copy and assignment.
- AsynchronousSocketCloseMonitor(const AsynchronousSocketCloseMonitor&);
- void operator=(const AsynchronousSocketCloseMonitor&);
-};
-
-#endif // ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
diff --git a/luni/src/main/native/IcuUtilities.cpp b/luni/src/main/native/IcuUtilities.cpp
index c1bdd0f..c6f3950 100644
--- a/luni/src/main/native/IcuUtilities.cpp
+++ b/luni/src/main/native/IcuUtilities.cpp
@@ -65,6 +65,8 @@
exceptionClass = "java/lang/ArrayIndexOutOfBoundsException";
} else if (error == U_UNSUPPORTED_ERROR) {
exceptionClass = "java/lang/UnsupportedOperationException";
+ } else if (error == U_FORMAT_INEXACT_ERROR) {
+ exceptionClass = "java/lang/ArithmeticException";
}
jniThrowExceptionFmt(env, exceptionClass, "%s failed: %s", function, u_errorName(error));
return true;
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
index 28e9fa5..6b720d4 100644
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -35,7 +35,7 @@
// 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 socket option.
+// 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/Portability.h b/luni/src/main/native/Portability.h
index 1c64b03..60b7062 100644
--- a/luni/src/main/native/Portability.h
+++ b/luni/src/main/native/Portability.h
@@ -74,14 +74,6 @@
#include <sys/sendfile.h>
#include <sys/statvfs.h>
-// The prebuilt toolchains tend to be rather old and don't include the newest
-// kernel headers. CAP_BLOCK_SUSPEND was introduced in 3.5, so until all of
-// headers update to at least that version, we need this hack.
-#ifndef CAP_BLOCK_SUSPEND
-#define CAP_BLOCK_SUSPEND 36
-#define CAP_LAST_CAP CAP_BLOCK_SUSPEND
-#endif // ifndef CAP_BLOCK_SUSPEND
-
#endif
#endif // PORTABILITY_H_included
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 68c59c3..5db47b5 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -33,6 +33,7 @@
ScopedLocalFrame localFrame(env);
#define REGISTER(FN) extern void FN(JNIEnv*); FN(env)
+ REGISTER(register_android_system_OsConstants);
REGISTER(register_java_io_Console);
REGISTER(register_java_io_File);
REGISTER(register_java_io_ObjectStreamClass);
@@ -70,7 +71,6 @@
REGISTER(register_libcore_icu_Transliterator);
REGISTER(register_libcore_io_AsynchronousCloseMonitor);
REGISTER(register_libcore_io_Memory);
- REGISTER(register_libcore_io_OsConstants);
REGISTER(register_libcore_io_Posix);
REGISTER(register_org_apache_harmony_dalvik_NativeTestTarget);
REGISTER(register_org_apache_harmony_xml_ExpatParser);
diff --git a/luni/src/main/native/libcore_io_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
similarity index 96%
rename from luni/src/main/native/libcore_io_OsConstants.cpp
rename to luni/src/main/native/android_system_OsConstants.cpp
index 1be201b..cc3054a 100644
--- a/luni/src/main/native/libcore_io_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -23,23 +23,29 @@
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
-#include <linux/if_addr.h>
-#include <linux/rtnetlink.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <signal.h>
#include <stdlib.h>
-#include <sys/capability.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
+// After the others because these are not necessarily self-contained in glibc.
+#include <linux/if_addr.h>
+#include <linux/rtnetlink.h>
+
#include <net/if.h> // After <sys/socket.h> to work around a Mac header file bug.
+#if defined(__BIONIC__)
+#include <linux/capability.h>
+#endif
+
static void initConstant(JNIEnv* env, jclass c, const char* fieldName, int value) {
jfieldID field = env->GetStaticFieldID(c, fieldName, "I");
env->SetStaticIntField(c, field, value);
@@ -59,6 +65,7 @@
#endif
initConstant(env, c, "AI_PASSIVE", AI_PASSIVE);
initConstant(env, c, "AI_V4MAPPED", AI_V4MAPPED);
+#if defined(CAP_LAST_CAP)
initConstant(env, c, "CAP_AUDIT_CONTROL", CAP_AUDIT_CONTROL);
initConstant(env, c, "CAP_AUDIT_WRITE", CAP_AUDIT_WRITE);
initConstant(env, c, "CAP_BLOCK_SUSPEND", CAP_BLOCK_SUSPEND);
@@ -97,6 +104,7 @@
initConstant(env, c, "CAP_SYS_TIME", CAP_SYS_TIME);
initConstant(env, c, "CAP_SYS_TTY_CONFIG", CAP_SYS_TTY_CONFIG);
initConstant(env, c, "CAP_WAKE_ALARM", CAP_WAKE_ALARM);
+#endif
initConstant(env, c, "E2BIG", E2BIG);
initConstant(env, c, "EACCES", EACCES);
initConstant(env, c, "EADDRINUSE", EADDRINUSE);
@@ -303,6 +311,18 @@
#if defined(MCAST_LEAVE_GROUP)
initConstant(env, c, "MCAST_LEAVE_GROUP", MCAST_LEAVE_GROUP);
#endif
+#if defined(MCAST_JOIN_SOURCE_GROUP)
+ initConstant(env, c, "MCAST_JOIN_SOURCE_GROUP", MCAST_JOIN_SOURCE_GROUP);
+#endif
+#if defined(MCAST_LEAVE_SOURCE_GROUP)
+ initConstant(env, c, "MCAST_LEAVE_SOURCE_GROUP", MCAST_LEAVE_SOURCE_GROUP);
+#endif
+#if defined(MCAST_BLOCK_SOURCE)
+ initConstant(env, c, "MCAST_BLOCK_SOURCE", MCAST_BLOCK_SOURCE);
+#endif
+#if defined(MCAST_UNBLOCK_SOURCE)
+ initConstant(env, c, "MCAST_UNBLOCK_SOURCE", MCAST_UNBLOCK_SOURCE);
+#endif
initConstant(env, c, "MCL_CURRENT", MCL_CURRENT);
initConstant(env, c, "MCL_FUTURE", MCL_FUTURE);
initConstant(env, c, "MSG_CTRUNC", MSG_CTRUNC);
@@ -342,6 +362,9 @@
initConstant(env, c, "POLLRDNORM", POLLRDNORM);
initConstant(env, c, "POLLWRBAND", POLLWRBAND);
initConstant(env, c, "POLLWRNORM", POLLWRNORM);
+#if defined(PR_SET_NO_NEW_PRIVS)
+ initConstant(env, c, "PR_SET_NO_NEW_PRIVS", PR_SET_NO_NEW_PRIVS);
+#endif
initConstant(env, c, "PROT_EXEC", PROT_EXEC);
initConstant(env, c, "PROT_NONE", PROT_NONE);
initConstant(env, c, "PROT_READ", PROT_READ);
@@ -564,6 +587,6 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(OsConstants, initConstants, "()V"),
};
-void register_libcore_io_OsConstants(JNIEnv* env) {
- jniRegisterNativeMethods(env, "libcore/io/OsConstants", gMethods, NELEM(gMethods));
+void register_android_system_OsConstants(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "android/system/OsConstants", gMethods, NELEM(gMethods));
}
diff --git a/luni/src/main/native/realpath.cpp b/luni/src/main/native/canonicalize_path.cpp
similarity index 98%
rename from luni/src/main/native/realpath.cpp
rename to luni/src/main/native/canonicalize_path.cpp
index d1960a4..b2a2a01 100644
--- a/luni/src/main/native/realpath.cpp
+++ b/luni/src/main/native/canonicalize_path.cpp
@@ -45,7 +45,7 @@
*
* This implementation also removes all the fixed-length buffers of the C original.
*/
-bool realpath(const char* path, std::string& resolved) {
+bool canonicalize_path(const char* path, std::string& resolved) {
// 'path' must be an absolute path.
if (path[0] != '/') {
errno = EINVAL;
diff --git a/luni/src/main/native/java_io_File.cpp b/luni/src/main/native/java_io_File.cpp
index c217ea2..046fc4f 100644
--- a/luni/src/main/native/java_io_File.cpp
+++ b/luni/src/main/native/java_io_File.cpp
@@ -22,7 +22,6 @@
#include "JniException.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
-#include "readlink.h"
#include "toStringArray.h"
#include <string>
@@ -39,96 +38,82 @@
#include <unistd.h>
#include <utime.h>
-static jstring File_readlink(JNIEnv* env, jclass, jstring javaPath) {
- ScopedUtfChars path(env, javaPath);
- if (path.c_str() == NULL) {
- return NULL;
- }
+static jstring File_canonicalizePath(JNIEnv* env, jclass, jstring javaPath) {
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return NULL;
+ }
- std::string result;
- if (!readlink(path.c_str(), result)) {
- jniThrowIOException(env, errno);
- return NULL;
- }
- return env->NewStringUTF(result.c_str());
-}
-
-static jstring File_realpath(JNIEnv* env, jclass, jstring javaPath) {
- ScopedUtfChars path(env, javaPath);
- if (path.c_str() == NULL) {
- return NULL;
- }
-
- extern bool realpath(const char* path, std::string& resolved);
- std::string result;
- if (!realpath(path.c_str(), result)) {
- jniThrowIOException(env, errno);
- return NULL;
- }
- return env->NewStringUTF(result.c_str());
+ extern bool canonicalize_path(const char* path, std::string& resolved);
+ std::string result;
+ if (!canonicalize_path(path.c_str(), result)) {
+ jniThrowIOException(env, errno);
+ return NULL;
+ }
+ return env->NewStringUTF(result.c_str());
}
static jboolean File_setLastModifiedImpl(JNIEnv* env, jclass, jstring javaPath, jlong ms) {
- ScopedUtfChars path(env, javaPath);
- if (path.c_str() == NULL) {
- return JNI_FALSE;
- }
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return JNI_FALSE;
+ }
- // We want to preserve the access time.
- struct stat sb;
- if (stat(path.c_str(), &sb) == -1) {
- return JNI_FALSE;
- }
+ // We want to preserve the access time.
+ struct stat sb;
+ if (stat(path.c_str(), &sb) == -1) {
+ return JNI_FALSE;
+ }
- // TODO: we could get microsecond resolution with utimes(3), "legacy" though it is.
- utimbuf times;
- times.actime = sb.st_atime;
- times.modtime = static_cast<time_t>(ms / 1000);
- return (utime(path.c_str(), ×) == 0);
+ // TODO: we could get microsecond resolution with utimes(3), "legacy" though it is.
+ utimbuf times;
+ times.actime = sb.st_atime;
+ times.modtime = static_cast<time_t>(ms / 1000);
+ return (utime(path.c_str(), ×) == 0);
}
// Iterates over the filenames in the given directory.
class ScopedReaddir {
-public:
- ScopedReaddir(const char* path) {
- mDirStream = opendir(path);
- mIsBad = (mDirStream == NULL);
+ public:
+ ScopedReaddir(const char* path) {
+ mDirStream = opendir(path);
+ mIsBad = (mDirStream == NULL);
+ }
+
+ ~ScopedReaddir() {
+ if (mDirStream != NULL) {
+ closedir(mDirStream);
}
+ }
- ~ScopedReaddir() {
- if (mDirStream != NULL) {
- closedir(mDirStream);
- }
+ // Returns the next filename, or NULL.
+ const char* next() {
+ if (mIsBad) {
+ return NULL;
}
-
- // Returns the next filename, or NULL.
- const char* next() {
- if (mIsBad) {
- return NULL;
- }
- errno = 0;
- dirent* result = readdir(mDirStream);
- if (result != NULL) {
- return result->d_name;
- }
- if (errno != 0) {
- mIsBad = true;
- }
- return NULL;
+ errno = 0;
+ dirent* result = readdir(mDirStream);
+ if (result != NULL) {
+ return result->d_name;
}
-
- // Has an error occurred on this stream?
- bool isBad() const {
- return mIsBad;
+ if (errno != 0) {
+ mIsBad = true;
}
+ return NULL;
+ }
-private:
- DIR* mDirStream;
- bool mIsBad;
+ // Has an error occurred on this stream?
+ bool isBad() const {
+ return mIsBad;
+ }
- // Disallow copy and assignment.
- ScopedReaddir(const ScopedReaddir&);
- void operator=(const ScopedReaddir&);
+ private:
+ DIR* mDirStream;
+ bool mIsBad;
+
+ // Disallow copy and assignment.
+ ScopedReaddir(const ScopedReaddir&);
+ void operator=(const ScopedReaddir&);
};
typedef std::vector<std::string> DirEntries;
@@ -136,38 +121,37 @@
// Reads the directory referred to by 'pathBytes', adding each directory entry
// to 'entries'.
static bool readDirectory(JNIEnv* env, jstring javaPath, DirEntries& entries) {
- ScopedUtfChars path(env, javaPath);
- if (path.c_str() == NULL) {
- return false;
- }
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return false;
+ }
- ScopedReaddir dir(path.c_str());
- const char* filename;
- while ((filename = dir.next()) != NULL) {
- if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0) {
- // TODO: this hides allocation failures from us. Push directory iteration up into Java?
- entries.push_back(filename);
- }
+ ScopedReaddir dir(path.c_str());
+ const char* filename;
+ while ((filename = dir.next()) != NULL) {
+ if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0) {
+ // TODO: this hides allocation failures from us. Push directory iteration up into Java?
+ entries.push_back(filename);
}
- return !dir.isBad();
+ }
+ return !dir.isBad();
}
static jobjectArray File_listImpl(JNIEnv* env, jclass, jstring javaPath) {
- // Read the directory entries into an intermediate form.
- DirEntries entries;
- if (!readDirectory(env, javaPath, entries)) {
- return NULL;
- }
- // Translate the intermediate form into a Java String[].
- return toStringArray(env, entries);
+ // Read the directory entries into an intermediate form.
+ DirEntries entries;
+ if (!readDirectory(env, javaPath, entries)) {
+ return NULL;
+ }
+ // Translate the intermediate form into a Java String[].
+ return toStringArray(env, entries);
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(File, listImpl, "(Ljava/lang/String;)[Ljava/lang/String;"),
- NATIVE_METHOD(File, readlink, "(Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(File, realpath, "(Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(File, setLastModifiedImpl, "(Ljava/lang/String;J)Z"),
+ NATIVE_METHOD(File, canonicalizePath, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(File, listImpl, "(Ljava/lang/String;)[Ljava/lang/String;"),
+ NATIVE_METHOD(File, setLastModifiedImpl, "(Ljava/lang/String;J)Z"),
};
void register_java_io_File(JNIEnv* env) {
- jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods));
+ jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods));
}
diff --git a/luni/src/main/native/java_lang_ProcessManager.cpp b/luni/src/main/native/java_lang_ProcessManager.cpp
index a74ba8b..3a97ac6 100644
--- a/luni/src/main/native/java_lang_ProcessManager.cpp
+++ b/luni/src/main/native/java_lang_ProcessManager.cpp
@@ -38,7 +38,7 @@
static void CloseNonStandardFds(int status_pipe_fd) {
// On Cygwin, Linux, and Solaris, the best way to close iterates over "/proc/self/fd/".
const char* fd_path = "/proc/self/fd";
-#ifdef __APPLE__
+#if defined(__APPLE__)
// On Mac OS, there's "/dev/fd/" which Linux seems to link to "/proc/self/fd/",
// but which on Solaris appears to be something quite different.
fd_path = "/dev/fd";
diff --git a/luni/src/main/native/java_lang_System.cpp b/luni/src/main/native/java_lang_System.cpp
index f8b98b1..944c0c3 100644
--- a/luni/src/main/native/java_lang_System.cpp
+++ b/luni/src/main/native/java_lang_System.cpp
@@ -34,6 +34,10 @@
#include <time.h>
#include <unistd.h>
+#if defined(HAVE_ANDROID_OS)
+extern "C" void android_get_LD_LIBRARY_PATH(char*, size_t);
+#endif
+
static void System_log(JNIEnv* env, jclass, jchar type, jstring javaMessage, jthrowable exception) {
ScopedUtfChars message(env, javaMessage);
if (message.c_str() == NULL) {
@@ -82,6 +86,18 @@
properties.push_back("android.zlib.version=" ZLIB_VERSION);
properties.push_back("android.openssl.version=" OPENSSL_VERSION_TEXT);
+ const char* library_path = getenv("LD_LIBRARY_PATH");
+#if defined(HAVE_ANDROID_OS)
+ if (library_path == NULL) {
+ android_get_LD_LIBRARY_PATH(path, sizeof(path));
+ library_path = path;
+ }
+#endif
+ if (library_path == NULL) {
+ library_path = "";
+ }
+ properties.push_back(std::string("java.library.path=") + library_path);
+
return toStringArray(env, properties);
}
diff --git a/luni/src/main/native/java_math_NativeBN.cpp b/luni/src/main/native/java_math_NativeBN.cpp
index 78cc69a..d0a03b7 100644
--- a/luni/src/main/native/java_math_NativeBN.cpp
+++ b/luni/src/main/native/java_math_NativeBN.cpp
@@ -108,18 +108,24 @@
throwExceptionIfNecessary(env);
}
-static void NativeBN_putULongInt(JNIEnv* env, jclass, jlong a0, unsigned long long dw, jboolean neg) {
+static void NativeBN_putULongInt(JNIEnv* env, jclass, jlong a0, jlong java_dw, jboolean neg) {
if (!oneValidHandle(env, a0)) return;
- unsigned int hi = dw >> 32; // This shifts without sign extension.
- int lo = (int)dw; // This truncates implicitly.
+
+ uint64_t dw = java_dw;
// cf. litEndInts2bn:
BIGNUM* a = toBigNum(a0);
bn_check_top(a);
- if (bn_wexpand(a, 2) != NULL) {
+ if (bn_wexpand(a, 8/BN_BYTES) != NULL) {
+#ifdef __LP64__
+ a->d[0] = dw;
+#else
+ unsigned int hi = dw >> 32; // This shifts without sign extension.
+ int lo = (int)dw; // This truncates implicitly.
a->d[0] = lo;
a->d[1] = hi;
- a->top = 2;
+#endif
+ a->top = 8 / BN_BYTES;
a->neg = neg;
bn_correct_top(a);
} else {
@@ -127,7 +133,7 @@
}
}
-static void NativeBN_putLongInt(JNIEnv* env, jclass cls, jlong a, long long dw) {
+static void NativeBN_putLongInt(JNIEnv* env, jclass cls, jlong a, jlong dw) {
if (dw >= 0) {
NativeBN_putULongInt(env, cls, a, dw, JNI_FALSE);
} else {
@@ -187,11 +193,25 @@
if (scopedArray.get() == NULL) {
return;
}
-
- static_assert(sizeof(BN_ULONG) == sizeof(jint), "BN_ULONG is not 32-bit!");
- const BN_ULONG* tmpInts = reinterpret_cast<const BN_ULONG*>(scopedArray.get());
- if ((tmpInts != NULL) && (bn_wexpand(ret, len) != NULL)) {
+#ifdef __LP64__
+ const int wlen = (len + 1) / 2;
+#else
+ const int wlen = len;
+#endif
+ const unsigned int* tmpInts = reinterpret_cast<const unsigned int*>(scopedArray.get());
+ if ((tmpInts != NULL) && (bn_wexpand(ret, wlen) != NULL)) {
+#ifdef __LP64__
+ if (len % 2) {
+ ret->d[wlen - 1] = tmpInts[--len];
+ }
+ if (len > 0) {
+ for (int i = len - 2; i >= 0; i -= 2) {
+ ret->d[i/2] = ((unsigned long long)tmpInts[i+1] << 32) | tmpInts[i];
+ }
+ }
+#else
int i = len; do { i--; ret->d[i] = tmpInts[i]; } while (i > 0);
+#endif
ret->top = len;
ret->neg = neg;
// need to call this due to clear byte at top if avoiding
@@ -207,35 +227,39 @@
}
-#define BYTES2INT(bytes, k) \
+#ifdef __LP64__
+#define BYTES2ULONG(bytes, k) \
+ ((bytes[k + 7] & 0xffULL) | (bytes[k + 6] & 0xffULL) << 8 | (bytes[k + 5] & 0xffULL) << 16 | (bytes[k + 4] & 0xffULL) << 24 | \
+ (bytes[k + 3] & 0xffULL) << 32 | (bytes[k + 2] & 0xffULL) << 40 | (bytes[k + 1] & 0xffULL) << 48 | (bytes[k + 0] & 0xffULL) << 56)
+#else
+#define BYTES2ULONG(bytes, k) \
((bytes[k + 3] & 0xff) | (bytes[k + 2] & 0xff) << 8 | (bytes[k + 1] & 0xff) << 16 | (bytes[k + 0] & 0xff) << 24)
-
+#endif
static void negBigEndianBytes2bn(JNIEnv*, jclass, const unsigned char* bytes, int bytesLen, jlong ret0) {
BIGNUM* ret = toBigNum(ret0);
- // We rely on: (BN_BITS2 == 32), i.e. BN_ULONG is unsigned int and has 4 bytes:
bn_check_top(ret);
// FIXME: assert bytesLen > 0
- int intLen = (bytesLen + 3) / 4;
+ int wLen = (bytesLen + BN_BYTES - 1) / BN_BYTES;
int firstNonzeroDigit = -2;
- if (bn_wexpand(ret, intLen) != NULL) {
+ if (bn_wexpand(ret, wLen) != NULL) {
BN_ULONG* d = ret->d;
BN_ULONG di;
- ret->top = intLen;
- int highBytes = bytesLen % 4;
+ ret->top = wLen;
+ int highBytes = bytesLen % BN_BYTES;
int k = bytesLen;
// Put bytes to the int array starting from the end of the byte array
int i = 0;
while (k > highBytes) {
- k -= 4;
- di = BYTES2INT(bytes, k);
+ k -= BN_BYTES;
+ di = BYTES2ULONG(bytes, k);
if (di != 0) {
d[i] = -di;
firstNonzeroDigit = i;
i++;
while (k > highBytes) {
- k -= 4;
- d[i] = ~BYTES2INT(bytes, k);
+ k -= BN_BYTES;
+ d[i] = ~BYTES2ULONG(bytes, k);
i++;
}
break;
@@ -259,6 +283,8 @@
d[i] = -di;
}
}
+ // The top may have superfluous zeros, so fix it.
+ bn_correct_top(ret);
}
}
@@ -287,28 +313,25 @@
throwExceptionIfNecessary(env);
}
-static long long NativeBN_longInt(JNIEnv* env, jclass, jlong a0) {
+static jlong NativeBN_longInt(JNIEnv* env, jclass, jlong a0) {
if (!oneValidHandle(env, a0)) return -1;
+
BIGNUM* a = toBigNum(a0);
bn_check_top(a);
- int intLen = a->top;
- BN_ULONG* d = a->d;
- switch (intLen) {
- case 0:
- return 0;
- case 1:
- if (!a->neg) {
- return d[0] & 0X00000000FFFFFFFFLL;
- } else {
- return -(d[0] & 0X00000000FFFFFFFFLL);
- }
- default:
- if (!a->neg) {
- return ((long long)d[1] << 32) | (d[0] & 0XFFFFFFFFLL);
- } else {
- return -(((long long)d[1] << 32) | (d[0] & 0XFFFFFFFFLL));
- }
+ int wLen = a->top;
+ if (wLen == 0) {
+ return 0;
}
+
+#ifdef __LP64__
+ jlong result = a->d[0];
+#else
+ jlong result = static_cast<jlong>(a->d[0]) & 0xffffffff;
+ if (wLen > 1) {
+ result |= static_cast<jlong>(a->d[1]) << 32;
+ }
+#endif
+ return a->neg ? -result : result;
}
static char* leadingZerosTrimmed(char* s) {
@@ -367,11 +390,11 @@
if (!oneValidHandle(env, a0)) return NULL;
BIGNUM* a = toBigNum(a0);
bn_check_top(a);
- int len = a->top;
- if (len == 0) {
+ int wLen = a->top;
+ if (wLen == 0) {
return NULL;
}
- jintArray result = env->NewIntArray(len);
+ jintArray result = env->NewIntArray(wLen * BN_BYTES/sizeof(unsigned int));
if (result == NULL) {
return NULL;
}
@@ -379,11 +402,15 @@
if (ints.get() == NULL) {
return NULL;
}
- BN_ULONG* ulongs = reinterpret_cast<BN_ULONG*>(ints.get());
- if (ulongs == NULL) {
+ unsigned int* uints = reinterpret_cast<unsigned int*>(ints.get());
+ if (uints == NULL) {
return NULL;
}
- int i = len; do { i--; ulongs[i] = a->d[i]; } while (i > 0);
+#ifdef __LP64__
+ int i = wLen; do { i--; uints[i*2+1] = a->d[i] >> 32; uints[i*2] = a->d[i]; } while (i > 0);
+#else
+ int i = wLen; do { i--; uints[i] = a->d[i]; } while (i > 0);
+#endif
return result;
}
@@ -403,15 +430,13 @@
}
static int NativeBN_bitLength(JNIEnv* env, jclass, jlong a0) {
-// We rely on: (BN_BITS2 == 32), i.e. BN_ULONG is unsigned int and has 4 bytes:
-//
if (!oneValidHandle(env, a0)) return JNI_FALSE;
BIGNUM* a = toBigNum(a0);
bn_check_top(a);
- int intLen = a->top;
- if (intLen == 0) return 0;
+ int wLen = a->top;
+ if (wLen == 0) return 0;
BN_ULONG* d = a->d;
- int i = intLen - 1;
+ int i = wLen - 1;
BN_ULONG msd = d[i]; // most significant digit
if (a->neg) {
// Handle negative values correctly:
@@ -420,7 +445,7 @@
do { i--; } while (!((i < 0) || (d[i] != 0)));
if (i < 0) msd--; // Only if all lower significant digits are 0 we decrement the most significant one.
}
- return (intLen - 1) * 32 + BN_num_bits_word(msd);
+ return (wLen - 1) * BN_BYTES * 8 + BN_num_bits_word(msd);
}
static jboolean NativeBN_BN_is_bit_set(JNIEnv* env, jclass, jlong a, int n) {
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 8f6e321..9dc1cae 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -168,26 +168,28 @@
static jstring ICU_languageTagForLocale(JNIEnv* env, jclass, jstring javaLocaleId) {
ScopedUtfChars localeID(env, javaLocaleId);
- // The conversion from an ICU locale ID to a BCP 47 tag will shrink
- // the size of the string unless there's an invalid language or a bad
- // parse (which will result in an x-lvariant private use subtag at
- // the end of the input).
- const size_t initialBufferSize = localeID.size();
+ // In most common cases, the BCP 47 tag will be the same size as the ICU
+ // locale ID
+ const size_t initialBufferSize = localeID.size() + 1;
std::vector<char> buffer(initialBufferSize);
UErrorCode status = U_ZERO_ERROR;
- while (true) {
- const size_t outputLength = uloc_toLanguageTag(localeID.c_str(),
- &buffer[0], buffer.size(), false /* strict */, &status);
- if (U_FAILURE(status)) {
- return NULL;
- }
+ const size_t outputLength = uloc_toLanguageTag(localeID.c_str(),
+ &buffer[0], buffer.size(), false /* strict */, &status);
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ buffer.resize(outputLength + 1);
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(localeID.c_str(), &buffer[0], buffer.size(),
+ false /* strict */, &status);
+ }
- if (outputLength == buffer.size()) {
- buffer.resize(buffer.size() << 1);
- } else {
- break;
- }
+ if (status == U_STRING_NOT_TERMINATED_WARNING) {
+ buffer.resize(buffer.size() + 1);
+ buffer[buffer.size() -1] = '\0';
+ }
+
+ if (maybeThrowIcuException(env, "ICU::languageTagForLocale", status)) {
+ return NULL;
}
return env->NewStringUTF(&buffer[0]);
@@ -203,6 +205,15 @@
return ucurr_getDefaultFractionDigits(icuCurrencyCode.getTerminatedBuffer(), &status);
}
+static jint ICU_getCurrencyNumericCode(JNIEnv* env, jclass, jstring javaCurrencyCode) {
+ ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
+ if (!currencyCode.valid()) {
+ return 0;
+ }
+ UnicodeString icuCurrencyCode(currencyCode.unicodeString());
+ return ucurr_getNumericCode(icuCurrencyCode.getTerminatedBuffer());
+}
+
// TODO: rewrite this with int32_t ucurr_forLocale(const char* locale, UChar* buff, int32_t buffCapacity, UErrorCode* ec)...
static jstring ICU_getCurrencyCode(JNIEnv* env, jclass, jstring javaCountryCode) {
UErrorCode status = U_ZERO_ERROR;
@@ -454,7 +465,7 @@
setCharField(env, obj, "percent", dfs.getSymbol(DecimalFormatSymbols::kPercentSymbol));
setCharField(env, obj, "perMill", dfs.getSymbol(DecimalFormatSymbols::kPerMillSymbol));
setCharField(env, obj, "monetarySeparator", dfs.getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol));
- setCharField(env, obj, "minusSign", dfs.getSymbol(DecimalFormatSymbols:: kMinusSignSymbol));
+ setStringField(env, obj, "minusSign", dfs.getSymbol(DecimalFormatSymbols:: kMinusSignSymbol));
setStringField(env, obj, "exponentSeparator", dfs.getSymbol(DecimalFormatSymbols::kExponentialSymbol));
setStringField(env, obj, "infinity", dfs.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
setStringField(env, obj, "NaN", dfs.getSymbol(DecimalFormatSymbols::kNaNSymbol));
@@ -750,6 +761,23 @@
return env->NewString(result.getBuffer(), result.length());
}
+static void ICU_setDefaultLocale(JNIEnv* env, jclass, jstring javaLocaleName) {
+ Locale locale = getLocale(env, javaLocaleName);
+ UErrorCode status = U_ZERO_ERROR;
+
+ // TODO: Should we check whether locale.isBogus() here ? ICU will
+ // accept bogus locales as the default without complaint. It
+ // shouldn't make a difference in practice, users that set a bogus
+ // locale as the default shouldn't have any realistic expectation that
+ // things like defaults etc. will work correctly.
+ Locale::setDefault(locale, status);
+ maybeThrowIcuException(env, "Locale::setDefault", status);
+}
+
+static jstring ICU_getDefaultLocale(JNIEnv* env, jclass) {
+ return env->NewStringUTF(Locale::getDefault().getName());
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ICU, addLikelySubtags, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(ICU, getAvailableBreakIteratorLocalesNative, "()[Ljava/lang/String;"),
@@ -764,7 +792,9 @@
NATIVE_METHOD(ICU, getCurrencyCode, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(ICU, getCurrencyDisplayName, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(ICU, getCurrencyFractionDigits, "(Ljava/lang/String;)I"),
+ NATIVE_METHOD(ICU, getCurrencyNumericCode, "(Ljava/lang/String;)I"),
NATIVE_METHOD(ICU, getCurrencySymbol, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(ICU, getDefaultLocale, "()Ljava/lang/String;"),
NATIVE_METHOD(ICU, getDisplayCountryNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(ICU, getDisplayLanguageNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(ICU, getDisplayScriptNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
@@ -779,6 +809,7 @@
NATIVE_METHOD(ICU, languageTagForLocale, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(ICU, localeForLanguageTag, "(Ljava/lang/String;Z)Ljava/lang/String;"),
NATIVE_METHOD(ICU, initLocaleDataNative, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"),
+ NATIVE_METHOD(ICU, setDefaultLocale, "(Ljava/lang/String;)V"),
NATIVE_METHOD(ICU, toLowerCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
};
diff --git a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
index 5d715c9..0c8c3c9 100644
--- a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
+++ b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
@@ -119,7 +119,7 @@
} \
return reinterpret_cast<uintptr_t>(it)
-static jint NativeBreakIterator_cloneImpl(JNIEnv* env, jclass, jlong address) {
+static jlong NativeBreakIterator_cloneImpl(JNIEnv* env, jclass, jlong address) {
BreakIteratorAccessor it(env, address);
return reinterpret_cast<uintptr_t>(it->clone());
}
@@ -143,19 +143,19 @@
return it->following(offset);
}
-static jint NativeBreakIterator_getCharacterInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
+static jlong NativeBreakIterator_getCharacterInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createCharacterInstance);
}
-static jint NativeBreakIterator_getLineInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
+static jlong NativeBreakIterator_getLineInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createLineInstance);
}
-static jint NativeBreakIterator_getSentenceInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
+static jlong NativeBreakIterator_getSentenceInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createSentenceInstance);
}
-static jint NativeBreakIterator_getWordInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
+static jlong NativeBreakIterator_getWordInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createWordInstance);
}
diff --git a/luni/src/main/native/libcore_icu_NativeCollation.cpp b/luni/src/main/native/libcore_icu_NativeCollation.cpp
index f265bb5..00ec9ae 100644
--- a/luni/src/main/native/libcore_icu_NativeCollation.cpp
+++ b/luni/src/main/native/libcore_icu_NativeCollation.cpp
@@ -16,24 +16,87 @@
#include "ScopedStringChars.h"
#include "ScopedUtfChars.h"
#include "UniquePtr.h"
-#include "ucol_imp.h"
#include "unicode/ucol.h"
#include "unicode/ucoleitr.h"
+#include <cutils/log.h>
+
+// Manages a UCollationElements instance along with the jchar
+// array it is iterating over. The associated array can be unpinned
+// only after a call to ucol_closeElements. This means we have to
+// keep a reference to the string (so that it isn't collected) and
+// make a call to GetStringChars to ensure the underlying array is
+// pinned.
+class CollationElements {
+public:
+ CollationElements()
+ : mElements(NULL), mString(NULL), mChars(NULL) {
+ }
+
+ UCollationElements* get() const {
+ return mElements;
+ }
+
+ // Starts a new iteration sequence over the string |string|. If
+ // we have a valid UCollationElements object, we call ucol_setText
+ // on it. Otherwise, we create a new object with the specified
+ // collator.
+ UErrorCode start(JNIEnv* env, jstring string, UCollator* collator) {
+ release(env, false /* don't close the collator */);
+ mChars = env->GetStringChars(string, NULL);
+ if (mChars != NULL) {
+ mString = static_cast<jstring>(env->NewGlobalRef(string));
+ const size_t size = env->GetStringLength(string);
+
+ UErrorCode status = U_ZERO_ERROR;
+ // If we don't have a UCollationElements object yet, create
+ // a new one. If we do, reset it.
+ if (mElements == NULL) {
+ mElements = ucol_openElements(collator, mChars, size, &status);
+ } else {
+ ucol_setText(mElements, mChars, size, &status);
+ }
+
+ return status;
+ }
+
+ return U_ILLEGAL_ARGUMENT_ERROR;
+ }
+
+ void release(JNIEnv* env, bool closeCollator) {
+ if (mElements != NULL && closeCollator) {
+ ucol_closeElements(mElements);
+ }
+
+ if (mChars != NULL) {
+ env->ReleaseStringChars(mString, mChars);
+ env->DeleteGlobalRef(mString);
+ mChars = NULL;
+ mString = NULL;
+ }
+ }
+
+private:
+ UCollationElements* mElements;
+ jstring mString;
+ const jchar* mChars;
+};
static UCollator* toCollator(jlong address) {
return reinterpret_cast<UCollator*>(static_cast<uintptr_t>(address));
}
-static UCollationElements* toCollationElements(jlong address) {
- return reinterpret_cast<UCollationElements*>(static_cast<uintptr_t>(address));
+static CollationElements* toCollationElements(jlong address) {
+ return reinterpret_cast<CollationElements*>(static_cast<uintptr_t>(address));
}
static void NativeCollation_closeCollator(JNIEnv*, jclass, jlong address) {
ucol_close(toCollator(address));
}
-static void NativeCollation_closeElements(JNIEnv*, jclass, jlong address) {
- ucol_closeElements(toCollationElements(address));
+static void NativeCollation_closeElements(JNIEnv* env, jclass, jlong address) {
+ CollationElements* elements = toCollationElements(address);
+ elements->release(env, true /* close collator */);
+ delete elements;
}
static jint NativeCollation_compare(JNIEnv* env, jclass, jlong address, jstring javaLhs, jstring javaRhs) {
@@ -60,18 +123,23 @@
if (source.get() == NULL) {
return -1;
}
- UErrorCode status = U_ZERO_ERROR;
- UCollationElements* result = ucol_openElements(toCollator(address), source.get(), source.size(), &status);
+
+ UniquePtr<CollationElements> ce(new CollationElements);
+ UErrorCode status = ce->start(env, javaSource, toCollator(address));
maybeThrowIcuException(env, "ucol_openElements", status);
- return static_cast<jlong>(reinterpret_cast<uintptr_t>(result));
+ if (status == U_ZERO_ERROR) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(ce.release()));
+ }
+
+ return 0L;
}
static jint NativeCollation_getMaxExpansion(JNIEnv*, jclass, jlong address, jint order) {
- return ucol_getMaxExpansion(toCollationElements(address), order);
+ return ucol_getMaxExpansion(toCollationElements(address)->get(), order);
}
static jint NativeCollation_getOffset(JNIEnv*, jclass, jlong address) {
- return ucol_getOffset(toCollationElements(address));
+ return ucol_getOffset(toCollationElements(address)->get());
}
static jstring NativeCollation_getRules(JNIEnv* env, jclass, jlong address) {
@@ -86,7 +154,8 @@
return NULL;
}
const UCollator* collator = toCollator(address);
- uint8_t byteArray[UCOL_MAX_BUFFER * 2];
+ // The buffer size prevents reallocation for most strings.
+ uint8_t byteArray[128];
UniquePtr<uint8_t[]> largerByteArray;
uint8_t* usedByteArray = byteArray;
size_t byteArraySize = ucol_getSortKey(collator, source.get(), source.size(), usedByteArray, sizeof(byteArray) - 1);
@@ -106,7 +175,7 @@
static jint NativeCollation_next(JNIEnv* env, jclass, jlong address) {
UErrorCode status = U_ZERO_ERROR;
- jint result = ucol_next(toCollationElements(address), &status);
+ jint result = ucol_next(toCollationElements(address)->get(), &status);
maybeThrowIcuException(env, "ucol_next", status);
return result;
}
@@ -136,13 +205,13 @@
static jint NativeCollation_previous(JNIEnv* env, jclass, jlong address) {
UErrorCode status = U_ZERO_ERROR;
- jint result = ucol_previous(toCollationElements(address), &status);
+ jint result = ucol_previous(toCollationElements(address)->get(), &status);
maybeThrowIcuException(env, "ucol_previous", status);
return result;
}
static void NativeCollation_reset(JNIEnv*, jclass, jlong address) {
- ucol_reset(toCollationElements(address));
+ ucol_reset(toCollationElements(address)->get());
}
static jlong NativeCollation_safeClone(JNIEnv* env, jclass, jlong address) {
@@ -160,7 +229,7 @@
static void NativeCollation_setOffset(JNIEnv* env, jclass, jlong address, jint offset) {
UErrorCode status = U_ZERO_ERROR;
- ucol_setOffset(toCollationElements(address), offset, &status);
+ ucol_setOffset(toCollationElements(address)->get(), offset, &status);
maybeThrowIcuException(env, "ucol_setOffset", status);
}
@@ -169,8 +238,7 @@
if (source.get() == NULL) {
return;
}
- UErrorCode status = U_ZERO_ERROR;
- ucol_setText(toCollationElements(address), source.get(), source.size(), &status);
+ UErrorCode status = toCollationElements(address)->start(env, javaSource, NULL);
maybeThrowIcuException(env, "ucol_setText", status);
}
@@ -179,7 +247,7 @@
NATIVE_METHOD(NativeCollation, closeElements, "(J)V"),
NATIVE_METHOD(NativeCollation, compare, "(JLjava/lang/String;Ljava/lang/String;)I"),
NATIVE_METHOD(NativeCollation, getAttribute, "(JI)I"),
- NATIVE_METHOD(NativeCollation, getCollationElementIterator, "(JLjava/lang/String;)I"),
+ NATIVE_METHOD(NativeCollation, getCollationElementIterator, "(JLjava/lang/String;)J"),
NATIVE_METHOD(NativeCollation, getMaxExpansion, "(JI)I"),
NATIVE_METHOD(NativeCollation, getOffset, "(J)I"),
NATIVE_METHOD(NativeCollation, getRules, "(J)Ljava/lang/String;"),
diff --git a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
index 32d13f3..c0fd42b 100644
--- a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
@@ -50,7 +50,7 @@
static DecimalFormatSymbols* makeDecimalFormatSymbols(JNIEnv* env,
jstring currencySymbol0, jchar decimalSeparator, jchar digit, jstring exponentSeparator0,
jchar groupingSeparator0, jstring infinity0,
- jstring internationalCurrencySymbol0, jchar minusSign,
+ jstring internationalCurrencySymbol0, jstring minusSign0,
jchar monetaryDecimalSeparator, jstring nan0, jchar patternSeparator,
jchar percent, jchar perMill, jchar zeroDigit) {
ScopedJavaUnicodeString currencySymbol(env, currencySymbol0);
@@ -58,6 +58,7 @@
ScopedJavaUnicodeString infinity(env, infinity0);
ScopedJavaUnicodeString internationalCurrencySymbol(env, internationalCurrencySymbol0);
ScopedJavaUnicodeString nan(env, nan0);
+ ScopedJavaUnicodeString minusSign(env, minusSign0);
UnicodeString groupingSeparator(groupingSeparator0);
DecimalFormatSymbols* result = new DecimalFormatSymbols;
@@ -69,7 +70,7 @@
result->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, groupingSeparator);
result->setSymbol(DecimalFormatSymbols::kInfinitySymbol, infinity.unicodeString());
result->setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, internationalCurrencySymbol.unicodeString());
- result->setSymbol(DecimalFormatSymbols::kMinusSignSymbol, UnicodeString(minusSign));
+ result->setSymbol(DecimalFormatSymbols::kMinusSignSymbol, minusSign.unicodeString());
result->setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, UnicodeString(monetaryDecimalSeparator));
result->setSymbol(DecimalFormatSymbols::kNaNSymbol, nan.unicodeString());
result->setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, UnicodeString(patternSeparator));
@@ -93,7 +94,7 @@
static void NativeDecimalFormat_setDecimalFormatSymbols(JNIEnv* env, jclass, jlong addr,
jstring currencySymbol, jchar decimalSeparator, jchar digit, jstring exponentSeparator,
jchar groupingSeparator, jstring infinity,
- jstring internationalCurrencySymbol, jchar minusSign,
+ jstring internationalCurrencySymbol, jstring minusSign,
jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator,
jchar percent, jchar perMill, jchar zeroDigit) {
DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
@@ -107,7 +108,7 @@
static jlong NativeDecimalFormat_open(JNIEnv* env, jclass, jstring pattern0,
jstring currencySymbol, jchar decimalSeparator, jchar digit, jstring exponentSeparator,
jchar groupingSeparator, jstring infinity,
- jstring internationalCurrencySymbol, jchar minusSign,
+ jstring internationalCurrencySymbol, jstring minusSign,
jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator,
jchar percent, jchar perMill, jchar zeroDigit) {
UErrorCode status = U_ZERO_ERROR;
@@ -367,10 +368,10 @@
NATIVE_METHOD(NativeDecimalFormat, formatDigitList, "(JLjava/lang/String;Llibcore/icu/NativeDecimalFormat$FieldPositionIterator;)[C"),
NATIVE_METHOD(NativeDecimalFormat, getAttribute, "(JI)I"),
NATIVE_METHOD(NativeDecimalFormat, getTextAttribute, "(JI)Ljava/lang/String;"),
- NATIVE_METHOD(NativeDecimalFormat, open, "(Ljava/lang/String;Ljava/lang/String;CCLjava/lang/String;CLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)J"),
+ NATIVE_METHOD(NativeDecimalFormat, open, "(Ljava/lang/String;Ljava/lang/String;CCLjava/lang/String;CLjava/lang/String;Ljava/lang/String;Ljava/lang/String;CLjava/lang/String;CCCC)J"),
NATIVE_METHOD(NativeDecimalFormat, parse, "(JLjava/lang/String;Ljava/text/ParsePosition;Z)Ljava/lang/Number;"),
NATIVE_METHOD(NativeDecimalFormat, setAttribute, "(JII)V"),
- NATIVE_METHOD(NativeDecimalFormat, setDecimalFormatSymbols, "(JLjava/lang/String;CCLjava/lang/String;CLjava/lang/String;Ljava/lang/String;CCLjava/lang/String;CCCC)V"),
+ NATIVE_METHOD(NativeDecimalFormat, setDecimalFormatSymbols, "(JLjava/lang/String;CCLjava/lang/String;CLjava/lang/String;Ljava/lang/String;Ljava/lang/String;CLjava/lang/String;CCCC)V"),
NATIVE_METHOD(NativeDecimalFormat, setRoundingMode, "(JID)V"),
NATIVE_METHOD(NativeDecimalFormat, setSymbol, "(JILjava/lang/String;)V"),
NATIVE_METHOD(NativeDecimalFormat, setTextAttribute, "(JILjava/lang/String;)V"),
diff --git a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
index 4f50ce5..a27e7b8 100644
--- a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
+++ b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
@@ -16,20 +16,20 @@
#define LOG_TAG "AsynchronousCloseMonitor"
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
#include "JNIHelp.h"
#include "JniConstants.h"
#include "jni.h"
static void AsynchronousCloseMonitor_signalBlockedThreads(JNIEnv* env, jclass, jobject javaFd) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
- AsynchronousSocketCloseMonitor::signalBlockedThreads(fd);
+ AsynchronousCloseMonitor::signalBlockedThreads(fd);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(AsynchronousCloseMonitor, signalBlockedThreads, "(Ljava/io/FileDescriptor;)V"),
};
void register_libcore_io_AsynchronousCloseMonitor(JNIEnv* env) {
- AsynchronousSocketCloseMonitor::init();
+ AsynchronousCloseMonitor::init();
jniRegisterNativeMethods(env, "libcore/io/AsynchronousCloseMonitor", gMethods, NELEM(gMethods));
}
diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp
index 2cef410..9edbfb8 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -38,8 +38,8 @@
#define LONG_ALIGNMENT_MASK 0x7
#define INT_ALIGNMENT_MASK 0x3
#define SHORT_ALIGNMENT_MASK 0x1
-#elif defined(__i386__) || defined(__x86_64__)
-// x86 can load anything at any alignment.
+#elif defined(__aarch64__) || defined(__i386__) || defined(__x86_64__)
+// These architectures can load anything at any alignment.
#define LONG_ALIGNMENT_MASK 0x0
#define INT_ALIGNMENT_MASK 0x0
#define SHORT_ALIGNMENT_MASK 0x0
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 67c871a..db1a886 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "Posix"
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
#include "cutils/log.h"
#include "ExecStrings.h"
#include "JNIHelp.h"
@@ -24,12 +24,13 @@
#include "JniException.h"
#include "NetworkUtilities.h"
#include "Portability.h"
+#include "readlink.h"
#include "ScopedBytes.h"
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "toStringArray.h"
+#include "UniquePtr.h"
#include <arpa/inet.h>
#include <errno.h>
@@ -43,6 +44,7 @@
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
@@ -67,31 +69,77 @@
};
/**
- * Used to retry networking system calls that can return EINTR. Unlike TEMP_FAILURE_RETRY,
- * this also handles the case where the reason for failure is that another thread called
- * Socket.close. This macro also throws exceptions on failure.
+ * Used to retry networking system calls that can be interrupted with a signal. Unlike
+ * TEMP_FAILURE_RETRY, this also handles the case where
+ * AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal a close() or
+ * Thread.interrupt(). Other signals that result in an EINTR result are ignored and the system call
+ * is retried.
*
- * Returns the result of 'exp', though a Java exception will be pending if the result is -1.
+ * Returns the result of the system call though a Java exception will be pending if the result is
+ * -1: a SocketException if signaled via AsynchronousCloseMonitor, or ErrnoException for other
+ * failures.
*/
#define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
return_type _rc = -1; \
do { \
+ bool _wasSignaled; \
+ int _syscallErrno; \
{ \
int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
- AsynchronousSocketCloseMonitor _monitor(_fd); \
+ AsynchronousCloseMonitor _monitor(_fd); \
_rc = syscall_name(_fd, __VA_ARGS__); \
+ _syscallErrno = errno; \
+ _wasSignaled = _monitor.wasSignaled(); \
} \
- if (_rc == -1) { \
- if (jniGetFDFromFileDescriptor(jni_env, java_fd) == -1) { \
- jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
+ if (_wasSignaled) { \
+ jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
+ break; \
+ } \
+ if (_rc == -1 && _syscallErrno != EINTR) { \
+ /* TODO: with a format string we could show the arguments too, like strace(1). */ \
+ throwErrnoException(jni_env, # syscall_name); \
+ break; \
+ } \
+ } while (_rc == -1); /* _syscallErrno == EINTR && !_wasSignaled */ \
+ _rc; })
+
+/**
+ * Used to retry system calls that can be interrupted with a signal. Unlike TEMP_FAILURE_RETRY, this
+ * also handles the case where AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal
+ * a close() or Thread.interrupt(). Other signals that result in an EINTR result are ignored and the
+ * system call is retried.
+ *
+ * Returns the result of the system call though a Java exception will be pending if the result is
+ * -1: an IOException if the file descriptor is already closed, a InterruptedIOException if signaled
+ * via AsynchronousCloseMonitor, or ErrnoException for other failures.
+ */
+#define IO_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
+ return_type _rc = -1; \
+ int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
+ if (_fd == -1) { \
+ jniThrowException(jni_env, "java/io/IOException", "File descriptor closed"); \
+ } else { \
+ do { \
+ bool _wasSignaled; \
+ int _syscallErrno; \
+ { \
+ int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
+ AsynchronousCloseMonitor _monitor(_fd); \
+ _rc = syscall_name(_fd, __VA_ARGS__); \
+ _syscallErrno = errno; \
+ _wasSignaled = _monitor.wasSignaled(); \
+ } \
+ if (_wasSignaled) { \
+ jniThrowException(jni_env, "java/io/InterruptedIOException", # syscall_name " interrupted"); \
break; \
- } else if (errno != EINTR) { \
+ } \
+ if (_rc == -1 && _syscallErrno != EINTR) { \
/* TODO: with a format string we could show the arguments too, like strace(1). */ \
throwErrnoException(jni_env, # syscall_name); \
break; \
} \
- } \
- } while (_rc == -1); \
+ } while (_rc == -1); /* && _syscallErrno == EINTR && !_wasSignaled */ \
+ } \
_rc; })
static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2,
@@ -906,6 +954,14 @@
throwIfMinusOne(env, "mkdir", TEMP_FAILURE_RETRY(mkdir(path.c_str(), mode)));
}
+static void Posix_mkfifo(JNIEnv* env, jobject, jstring javaPath, jint mode) {
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return;
+ }
+ throwIfMinusOne(env, "mkfifo", TEMP_FAILURE_RETRY(mkfifo(path.c_str(), mode)));
+}
+
static void Posix_mlock(JNIEnv* env, jobject, jlong address, jlong byteCount) {
void* ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(address));
throwIfMinusOne(env, "mlock", TEMP_FAILURE_RETRY(mlock(ptr, byteCount)));
@@ -970,7 +1026,7 @@
static jfieldID eventsFid = env->GetFieldID(JniConstants::structPollfdClass, "events", "S");
static jfieldID reventsFid = env->GetFieldID(JniConstants::structPollfdClass, "revents", "S");
- // Turn the Java libcore.io.StructPollfd[] into a C++ struct pollfd[].
+ // Turn the Java android.system.StructPollfd[] into a C++ struct pollfd[].
size_t arrayLength = env->GetArrayLength(javaStructs);
UniquePtr<struct pollfd[]> fds(new struct pollfd[arrayLength]);
memset(fds.get(), 0, sizeof(struct pollfd) * arrayLength);
@@ -989,11 +1045,9 @@
++count;
}
- // Since we don't know which fds -- if any -- are sockets, be conservative and register
- // all fds for asynchronous socket close monitoring.
- std::vector<AsynchronousSocketCloseMonitor*> monitors;
+ std::vector<AsynchronousCloseMonitor*> monitors;
for (size_t i = 0; i < count; ++i) {
- monitors.push_back(new AsynchronousSocketCloseMonitor(fds[i].fd));
+ monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd));
}
int rc = poll(fds.get(), count, timeoutMs);
for (size_t i = 0; i < monitors.size(); ++i) {
@@ -1004,7 +1058,7 @@
return -1;
}
- // Update the revents fields in the Java libcore.io.StructPollfd[].
+ // Update the revents fields in the Java android.system.StructPollfd[].
for (size_t i = 0; i < count; ++i) {
ScopedLocalRef<jobject> javaStruct(env, env->GetObjectArrayElement(javaStructs, i));
if (javaStruct.get() == NULL) {
@@ -1015,13 +1069,27 @@
return rc;
}
+static void Posix_posix_fallocate(JNIEnv* env, jobject, jobject javaFd, jlong offset, jlong length) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ errno = TEMP_FAILURE_RETRY(posix_fallocate64(fd, offset, length));
+ if (errno != 0) {
+ throwErrnoException(env, "posix_fallocate");
+ }
+}
+
+static jint Posix_prctl(JNIEnv* env, jobject, jint option, jlong arg2, jlong arg3, jlong arg4, jlong arg5) {
+ int result = prctl(static_cast<int>(option),
+ static_cast<unsigned long>(arg2), static_cast<unsigned long>(arg3),
+ static_cast<unsigned long>(arg4), static_cast<unsigned long>(arg5));
+ return throwIfMinusOne(env, "prctl", result);
+}
+
static jint Posix_preadBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jlong offset) {
ScopedBytesRW bytes(env, javaBytes);
if (bytes.get() == NULL) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "pread", TEMP_FAILURE_RETRY(pread64(fd, bytes.get() + byteOffset, byteCount, offset)));
+ return IO_FAILURE_RETRY(env, ssize_t, pread64, javaFd, bytes.get() + byteOffset, byteCount, offset);
}
static jint Posix_pwriteBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount, jlong offset) {
@@ -1029,8 +1097,7 @@
if (bytes.get() == NULL) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "pwrite", TEMP_FAILURE_RETRY(pwrite64(fd, bytes.get() + byteOffset, byteCount, offset)));
+ return IO_FAILURE_RETRY(env, ssize_t, pwrite64, javaFd, bytes.get() + byteOffset, byteCount, offset);
}
static jint Posix_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) {
@@ -1038,8 +1105,21 @@
if (bytes.get() == NULL) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "read", TEMP_FAILURE_RETRY(read(fd, bytes.get() + byteOffset, byteCount)));
+ return IO_FAILURE_RETRY(env, ssize_t, read, javaFd, bytes.get() + byteOffset, byteCount);
+}
+
+static jstring Posix_readlink(JNIEnv* env, jobject, jstring javaPath) {
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return NULL;
+ }
+
+ std::string result;
+ if (!readlink(path.c_str(), result)) {
+ throwErrnoException(env, "readlink");
+ return NULL;
+ }
+ return env->NewStringUTF(result.c_str());
}
static jint Posix_readv(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffers, jintArray offsets, jintArray byteCounts) {
@@ -1047,8 +1127,7 @@
if (!ioVec.init(buffers, offsets, byteCounts)) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "readv", TEMP_FAILURE_RETRY(readv(fd, ioVec.get(), ioVec.size())));
+ return IO_FAILURE_RETRY(env, ssize_t, readv, javaFd, ioVec.get(), ioVec.size());
}
static jint Posix_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetSocketAddress) {
@@ -1170,6 +1249,7 @@
// Mac OS didn't support modern multicast APIs until 10.7.
static void Posix_setsockoptIpMreqn(JNIEnv*, jobject, jobject, jint, jint, jint) { abort(); }
static void Posix_setsockoptGroupReq(JNIEnv*, jobject, jobject, jint, jint, jobject) { abort(); }
+static void Posix_setsockoptGroupSourceReq(JNIEnv*, jobject, jobject, jint, jint, jobject) { abort(); }
#else
static void Posix_setsockoptIpMreqn(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) {
ip_mreqn req;
@@ -1198,6 +1278,7 @@
if (rc == -1 && errno == EINVAL) {
// Maybe we're a 32-bit binary talking to a 64-bit kernel?
// glibc doesn't automatically handle this.
+ // http://sourceware.org/bugzilla/show_bug.cgi?id=12080
struct group_req64 {
uint32_t gr_interface;
uint32_t my_padding;
@@ -1210,6 +1291,48 @@
}
throwIfMinusOne(env, "setsockopt", rc);
}
+
+static void Posix_setsockoptGroupSourceReq(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaGroupSourceReq) {
+ socklen_t sa_len;
+ struct group_source_req req;
+ memset(&req, 0, sizeof(req));
+
+ static jfieldID gsrInterfaceFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_interface", "I");
+ req.gsr_interface = env->GetIntField(javaGroupSourceReq, gsrInterfaceFid);
+ // Get the IPv4 or IPv6 multicast address to join or leave.
+ static jfieldID gsrGroupFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_group", "Ljava/net/InetAddress;");
+ ScopedLocalRef<jobject> javaGroup(env, env->GetObjectField(javaGroupSourceReq, gsrGroupFid));
+ if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, req.gsr_group, sa_len)) {
+ return;
+ }
+
+ // Get the IPv4 or IPv6 multicast address to add to the filter.
+ static jfieldID gsrSourceFid = env->GetFieldID(JniConstants::structGroupSourceReqClass, "gsr_source", "Ljava/net/InetAddress;");
+ ScopedLocalRef<jobject> javaSource(env, env->GetObjectField(javaGroupSourceReq, gsrSourceFid));
+ if (!inetAddressToSockaddrVerbatim(env, javaSource.get(), 0, req.gsr_source, sa_len)) {
+ return;
+ }
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req, sizeof(req)));
+ if (rc == -1 && errno == EINVAL) {
+ // Maybe we're a 32-bit binary talking to a 64-bit kernel?
+ // glibc doesn't automatically handle this.
+ // http://sourceware.org/bugzilla/show_bug.cgi?id=12080
+ struct group_source_req64 {
+ uint32_t gsr_interface;
+ uint32_t my_padding;
+ sockaddr_storage gsr_group;
+ sockaddr_storage gsr_source;
+ };
+ group_source_req64 req64;
+ req64.gsr_interface = req.gsr_interface;
+ memcpy(&req64.gsr_group, &req.gsr_group, sizeof(req.gsr_group));
+ memcpy(&req64.gsr_source, &req.gsr_source, sizeof(req.gsr_source));
+ rc = TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &req64, sizeof(req64)));
+ }
+ throwIfMinusOne(env, "setsockopt", rc);
+}
#endif
static void Posix_setsockoptLinger(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jobject javaLinger) {
@@ -1350,8 +1473,7 @@
if (bytes.get() == NULL) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "write", TEMP_FAILURE_RETRY(write(fd, bytes.get() + byteOffset, byteCount)));
+ return IO_FAILURE_RETRY(env, ssize_t, write, javaFd, bytes.get() + byteOffset, byteCount);
}
static jint Posix_writev(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffers, jintArray offsets, jintArray byteCounts) {
@@ -1359,8 +1481,7 @@
if (!ioVec.init(buffers, offsets, byteCounts)) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "writev", TEMP_FAILURE_RETRY(writev(fd, ioVec.get(), ioVec.size())));
+ return IO_FAILURE_RETRY(env, ssize_t, writev, javaFd, ioVec.get(), ioVec.size());
}
static JNINativeMethod gMethods[] = {
@@ -1380,14 +1501,14 @@
NATIVE_METHOD(Posix, fchown, "(Ljava/io/FileDescriptor;II)V"),
NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"),
NATIVE_METHOD(Posix, fcntlLong, "(Ljava/io/FileDescriptor;IJ)I"),
- NATIVE_METHOD(Posix, fcntlFlock, "(Ljava/io/FileDescriptor;ILlibcore/io/StructFlock;)I"),
+ NATIVE_METHOD(Posix, fcntlFlock, "(Ljava/io/FileDescriptor;ILandroid/system/StructFlock;)I"),
NATIVE_METHOD(Posix, fdatasync, "(Ljava/io/FileDescriptor;)V"),
- NATIVE_METHOD(Posix, fstat, "(Ljava/io/FileDescriptor;)Llibcore/io/StructStat;"),
- NATIVE_METHOD(Posix, fstatvfs, "(Ljava/io/FileDescriptor;)Llibcore/io/StructStatVfs;"),
+ NATIVE_METHOD(Posix, fstat, "(Ljava/io/FileDescriptor;)Landroid/system/StructStat;"),
+ NATIVE_METHOD(Posix, fstatvfs, "(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;"),
NATIVE_METHOD(Posix, fsync, "(Ljava/io/FileDescriptor;)V"),
NATIVE_METHOD(Posix, ftruncate, "(Ljava/io/FileDescriptor;J)V"),
NATIVE_METHOD(Posix, gai_strerror, "(I)Ljava/lang/String;"),
- NATIVE_METHOD(Posix, getaddrinfo, "(Ljava/lang/String;Llibcore/io/StructAddrinfo;)[Ljava/net/InetAddress;"),
+ NATIVE_METHOD(Posix, getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;)[Ljava/net/InetAddress;"),
NATIVE_METHOD(Posix, getegid, "()I"),
NATIVE_METHOD(Posix, geteuid, "()I"),
NATIVE_METHOD(Posix, getgid, "()I"),
@@ -1396,29 +1517,30 @@
NATIVE_METHOD(Posix, getpeername, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"),
NATIVE_METHOD(Posix, getpid, "()I"),
NATIVE_METHOD(Posix, getppid, "()I"),
- NATIVE_METHOD(Posix, getpwnam, "(Ljava/lang/String;)Llibcore/io/StructPasswd;"),
- NATIVE_METHOD(Posix, getpwuid, "(I)Llibcore/io/StructPasswd;"),
+ NATIVE_METHOD(Posix, getpwnam, "(Ljava/lang/String;)Landroid/system/StructPasswd;"),
+ NATIVE_METHOD(Posix, getpwuid, "(I)Landroid/system/StructPasswd;"),
NATIVE_METHOD(Posix, getsockname, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"),
NATIVE_METHOD(Posix, getsockoptByte, "(Ljava/io/FileDescriptor;II)I"),
NATIVE_METHOD(Posix, getsockoptInAddr, "(Ljava/io/FileDescriptor;II)Ljava/net/InetAddress;"),
NATIVE_METHOD(Posix, getsockoptInt, "(Ljava/io/FileDescriptor;II)I"),
- NATIVE_METHOD(Posix, getsockoptLinger, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructLinger;"),
- NATIVE_METHOD(Posix, getsockoptTimeval, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructTimeval;"),
- NATIVE_METHOD(Posix, getsockoptUcred, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructUcred;"),
+ NATIVE_METHOD(Posix, getsockoptLinger, "(Ljava/io/FileDescriptor;II)Landroid/system/StructLinger;"),
+ NATIVE_METHOD(Posix, getsockoptTimeval, "(Ljava/io/FileDescriptor;II)Landroid/system/StructTimeval;"),
+ NATIVE_METHOD(Posix, getsockoptUcred, "(Ljava/io/FileDescriptor;II)Landroid/system/StructUcred;"),
NATIVE_METHOD(Posix, gettid, "()I"),
NATIVE_METHOD(Posix, getuid, "()I"),
NATIVE_METHOD(Posix, if_indextoname, "(I)Ljava/lang/String;"),
NATIVE_METHOD(Posix, inet_pton, "(ILjava/lang/String;)Ljava/net/InetAddress;"),
NATIVE_METHOD(Posix, ioctlInetAddress, "(Ljava/io/FileDescriptor;ILjava/lang/String;)Ljava/net/InetAddress;"),
- NATIVE_METHOD(Posix, ioctlInt, "(Ljava/io/FileDescriptor;ILlibcore/util/MutableInt;)I"),
+ NATIVE_METHOD(Posix, ioctlInt, "(Ljava/io/FileDescriptor;ILandroid/util/MutableInt;)I"),
NATIVE_METHOD(Posix, isatty, "(Ljava/io/FileDescriptor;)Z"),
NATIVE_METHOD(Posix, kill, "(II)V"),
NATIVE_METHOD(Posix, lchown, "(Ljava/lang/String;II)V"),
NATIVE_METHOD(Posix, listen, "(Ljava/io/FileDescriptor;I)V"),
NATIVE_METHOD(Posix, lseek, "(Ljava/io/FileDescriptor;JI)J"),
- NATIVE_METHOD(Posix, lstat, "(Ljava/lang/String;)Llibcore/io/StructStat;"),
+ NATIVE_METHOD(Posix, lstat, "(Ljava/lang/String;)Landroid/system/StructStat;"),
NATIVE_METHOD(Posix, mincore, "(JJ[B)V"),
NATIVE_METHOD(Posix, mkdir, "(Ljava/lang/String;I)V"),
+ NATIVE_METHOD(Posix, mkfifo, "(Ljava/lang/String;I)V"),
NATIVE_METHOD(Posix, mlock, "(JJ)V"),
NATIVE_METHOD(Posix, mmap, "(JJIILjava/io/FileDescriptor;J)J"),
NATIVE_METHOD(Posix, msync, "(JJI)V"),
@@ -1426,15 +1548,18 @@
NATIVE_METHOD(Posix, munmap, "(JJ)V"),
NATIVE_METHOD(Posix, open, "(Ljava/lang/String;II)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, pipe, "()[Ljava/io/FileDescriptor;"),
- NATIVE_METHOD(Posix, poll, "([Llibcore/io/StructPollfd;I)I"),
+ NATIVE_METHOD(Posix, poll, "([Landroid/system/StructPollfd;I)I"),
+ NATIVE_METHOD(Posix, posix_fallocate, "(Ljava/io/FileDescriptor;JJ)V"),
+ NATIVE_METHOD(Posix, prctl, "(IJJJJ)I"),
NATIVE_METHOD(Posix, preadBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIJ)I"),
NATIVE_METHOD(Posix, pwriteBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIJ)I"),
NATIVE_METHOD(Posix, readBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),
+ NATIVE_METHOD(Posix, readlink, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(Posix, readv, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
NATIVE_METHOD(Posix, recvfromBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I"),
NATIVE_METHOD(Posix, remove, "(Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, rename, "(Ljava/lang/String;Ljava/lang/String;)V"),
- NATIVE_METHOD(Posix, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Llibcore/util/MutableLong;J)J"),
+ NATIVE_METHOD(Posix, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/util/MutableLong;J)J"),
NATIVE_METHOD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"),
NATIVE_METHOD(Posix, setegid, "(I)V"),
NATIVE_METHOD(Posix, setenv, "(Ljava/lang/String;Ljava/lang/String;Z)V"),
@@ -1445,15 +1570,16 @@
NATIVE_METHOD(Posix, setsockoptIfreq, "(Ljava/io/FileDescriptor;IILjava/lang/String;)V"),
NATIVE_METHOD(Posix, setsockoptInt, "(Ljava/io/FileDescriptor;III)V"),
NATIVE_METHOD(Posix, setsockoptIpMreqn, "(Ljava/io/FileDescriptor;III)V"),
- NATIVE_METHOD(Posix, setsockoptGroupReq, "(Ljava/io/FileDescriptor;IILlibcore/io/StructGroupReq;)V"),
- NATIVE_METHOD(Posix, setsockoptLinger, "(Ljava/io/FileDescriptor;IILlibcore/io/StructLinger;)V"),
- NATIVE_METHOD(Posix, setsockoptTimeval, "(Ljava/io/FileDescriptor;IILlibcore/io/StructTimeval;)V"),
+ NATIVE_METHOD(Posix, setsockoptGroupReq, "(Ljava/io/FileDescriptor;IILandroid/system/StructGroupReq;)V"),
+ NATIVE_METHOD(Posix, setsockoptGroupSourceReq, "(Ljava/io/FileDescriptor;IILandroid/system/StructGroupSourceReq;)V"),
+ NATIVE_METHOD(Posix, setsockoptLinger, "(Ljava/io/FileDescriptor;IILandroid/system/StructLinger;)V"),
+ NATIVE_METHOD(Posix, setsockoptTimeval, "(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V"),
NATIVE_METHOD(Posix, setuid, "(I)V"),
NATIVE_METHOD(Posix, shutdown, "(Ljava/io/FileDescriptor;I)V"),
NATIVE_METHOD(Posix, socket, "(III)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, socketpair, "(IIILjava/io/FileDescriptor;Ljava/io/FileDescriptor;)V"),
- NATIVE_METHOD(Posix, stat, "(Ljava/lang/String;)Llibcore/io/StructStat;"),
- NATIVE_METHOD(Posix, statvfs, "(Ljava/lang/String;)Llibcore/io/StructStatVfs;"),
+ NATIVE_METHOD(Posix, stat, "(Ljava/lang/String;)Landroid/system/StructStat;"),
+ NATIVE_METHOD(Posix, statvfs, "(Ljava/lang/String;)Landroid/system/StructStatVfs;"),
NATIVE_METHOD(Posix, strerror, "(I)Ljava/lang/String;"),
NATIVE_METHOD(Posix, strsignal, "(I)Ljava/lang/String;"),
NATIVE_METHOD(Posix, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"),
@@ -1461,9 +1587,9 @@
NATIVE_METHOD(Posix, tcdrain, "(Ljava/io/FileDescriptor;)V"),
NATIVE_METHOD(Posix, tcsendbreak, "(Ljava/io/FileDescriptor;I)V"),
NATIVE_METHOD(Posix, umaskImpl, "(I)I"),
- NATIVE_METHOD(Posix, uname, "()Llibcore/io/StructUtsname;"),
+ NATIVE_METHOD(Posix, uname, "()Landroid/system/StructUtsname;"),
NATIVE_METHOD(Posix, unsetenv, "(Ljava/lang/String;)V"),
- NATIVE_METHOD(Posix, waitpid, "(ILlibcore/util/MutableInt;I)I"),
+ NATIVE_METHOD(Posix, waitpid, "(ILandroid/util/MutableInt;I)I"),
NATIVE_METHOD(Posix, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),
NATIVE_METHOD(Posix, writev, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)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 6ba055a..2ea8806 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -632,7 +632,8 @@
parsingContext->stringStack.push(env, uri);
parsingContext->stringStack.push(env, localName);
- env->CallVoidMethod(javaParser, startElementMethod, uri, localName, qName, attributes, count);
+ jlong attributesAddress = reinterpret_cast<jlong>(attributes);
+ env->CallVoidMethod(javaParser, startElementMethod, uri, localName, qName, attributesAddress, count);
parsingContext->attributes = NULL;
parsingContext->attributeCount = -1;
@@ -1291,7 +1292,7 @@
static void ExpatParser_staticInitialize(JNIEnv* env, jobject classObject, jstring empty) {
jclass clazz = reinterpret_cast<jclass>(classObject);
startElementMethod = env->GetMethodID(clazz, "startElement",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V");
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JI)V");
if (startElementMethod == NULL) return;
endElementMethod = env->GetMethodID(clazz, "endElement",
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index 4e10cc7..9ca5054 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -4,69 +4,69 @@
# or BUILD_*_LIBRARY.
LOCAL_SRC_FILES := \
- AsynchronousSocketCloseMonitor.cpp \
- ExecStrings.cpp \
- IcuUtilities.cpp \
- JniException.cpp \
- NetworkUtilities.cpp \
- Register.cpp \
- ZipUtilities.cpp \
- cbigint.cpp \
- java_io_Console.cpp \
- java_io_File.cpp \
- java_io_ObjectStreamClass.cpp \
- java_lang_Character.cpp \
- java_lang_Double.cpp \
- java_lang_Float.cpp \
- java_lang_Math.cpp \
- java_lang_ProcessManager.cpp \
- java_lang_RealToString.cpp \
- java_lang_StrictMath.cpp \
- java_lang_StringToReal.cpp \
- java_lang_System.cpp \
- java_math_NativeBN.cpp \
- java_nio_ByteOrder.cpp \
- java_nio_charset_Charsets.cpp \
- java_text_Bidi.cpp \
- java_util_jar_StrictJarFile.cpp \
- java_util_regex_Matcher.cpp \
- java_util_regex_Pattern.cpp \
- java_util_zip_Adler32.cpp \
- java_util_zip_CRC32.cpp \
- java_util_zip_Deflater.cpp \
- java_util_zip_Inflater.cpp \
- libcore_icu_AlphabeticIndex.cpp \
- libcore_icu_DateIntervalFormat.cpp \
- libcore_icu_ICU.cpp \
- libcore_icu_NativeBreakIterator.cpp \
- libcore_icu_NativeCollation.cpp \
- libcore_icu_NativeConverter.cpp \
- libcore_icu_NativeDecimalFormat.cpp \
- libcore_icu_NativeIDN.cpp \
- libcore_icu_NativeNormalizer.cpp \
- libcore_icu_NativePluralRules.cpp \
- libcore_icu_TimeZoneNames.cpp \
- libcore_icu_Transliterator.cpp \
- libcore_io_AsynchronousCloseMonitor.cpp \
- libcore_io_Memory.cpp \
- libcore_io_OsConstants.cpp \
- libcore_io_Posix.cpp \
- org_apache_harmony_xml_ExpatParser.cpp \
- readlink.cpp \
- realpath.cpp \
- sun_misc_Unsafe.cpp \
- valueOf.cpp
+ AsynchronousCloseMonitor.cpp \
+ ExecStrings.cpp \
+ IcuUtilities.cpp \
+ JniException.cpp \
+ NetworkUtilities.cpp \
+ Register.cpp \
+ ZipUtilities.cpp \
+ android_system_OsConstants.cpp \
+ canonicalize_path.cpp \
+ cbigint.cpp \
+ java_io_Console.cpp \
+ java_io_File.cpp \
+ java_io_ObjectStreamClass.cpp \
+ java_lang_Character.cpp \
+ java_lang_Double.cpp \
+ java_lang_Float.cpp \
+ java_lang_Math.cpp \
+ java_lang_ProcessManager.cpp \
+ java_lang_RealToString.cpp \
+ java_lang_StrictMath.cpp \
+ java_lang_StringToReal.cpp \
+ java_lang_System.cpp \
+ java_math_NativeBN.cpp \
+ java_nio_ByteOrder.cpp \
+ java_nio_charset_Charsets.cpp \
+ java_text_Bidi.cpp \
+ java_util_jar_StrictJarFile.cpp \
+ java_util_regex_Matcher.cpp \
+ java_util_regex_Pattern.cpp \
+ java_util_zip_Adler32.cpp \
+ java_util_zip_CRC32.cpp \
+ java_util_zip_Deflater.cpp \
+ java_util_zip_Inflater.cpp \
+ libcore_icu_AlphabeticIndex.cpp \
+ libcore_icu_DateIntervalFormat.cpp \
+ libcore_icu_ICU.cpp \
+ libcore_icu_NativeBreakIterator.cpp \
+ libcore_icu_NativeCollation.cpp \
+ libcore_icu_NativeConverter.cpp \
+ libcore_icu_NativeDecimalFormat.cpp \
+ libcore_icu_NativeIDN.cpp \
+ libcore_icu_NativeNormalizer.cpp \
+ libcore_icu_NativePluralRules.cpp \
+ libcore_icu_TimeZoneNames.cpp \
+ libcore_icu_Transliterator.cpp \
+ libcore_io_AsynchronousCloseMonitor.cpp \
+ libcore_io_Memory.cpp \
+ libcore_io_Posix.cpp \
+ org_apache_harmony_xml_ExpatParser.cpp \
+ readlink.cpp \
+ sun_misc_Unsafe.cpp \
+ valueOf.cpp \
LOCAL_C_INCLUDES += \
- external/icu4c/common \
- external/icu4c/i18n \
- external/openssl/include \
- external/zlib \
- system/core/include
+ external/icu4c/common \
+ external/icu4c/i18n \
+ external/openssl/include \
+ external/zlib \
+ system/core/include \
LOCAL_STATIC_LIBRARIES += \
- libfdlibm
+ libfdlibm \
LOCAL_SHARED_LIBRARIES += \
- liblog \
- libnativehelper
+ liblog \
+ libnativehelper \
diff --git a/luni/src/test/java/dalvik/system/VMRuntimeTest.java b/luni/src/test/java/dalvik/system/VMRuntimeTest.java
new file mode 100644
index 0000000..44af461
--- /dev/null
+++ b/luni/src/test/java/dalvik/system/VMRuntimeTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 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 dalvik.system;
+
+import java.lang.reflect.Array;
+import junit.framework.TestCase;
+
+/**
+ * Test VMRuntime behavior.
+ */
+public final class VMRuntimeTest extends TestCase {
+
+ private void doTestNewNonMovableArray(Class<?> componentType, int step, int maxLength) {
+ // Can't create negative sized arrays.
+ try {
+ Object array = VMRuntime.getRuntime().newNonMovableArray(componentType, -1);
+ assertTrue(false);
+ } catch (NegativeArraySizeException expected) {
+ }
+
+ try {
+ Object array = VMRuntime.getRuntime().newNonMovableArray(componentType, Integer.MIN_VALUE);
+ assertTrue(false);
+ } catch (NegativeArraySizeException expected) {
+ }
+
+ // Allocate arrays in a loop and check their properties.
+ for (int i = 0; i <= maxLength; i += step) {
+ Object array = VMRuntime.getRuntime().newNonMovableArray(componentType, i);
+ assertTrue(array.getClass().isArray());
+ assertEquals(array.getClass().getComponentType(), componentType);
+ assertEquals(Array.getLength(array), i);
+ }
+ }
+
+ public void testNewNonMovableArray() {
+ // Can't create arrays with no component type.
+ try {
+ Object array = VMRuntime.getRuntime().newNonMovableArray(null, 0);
+ assertTrue(false);
+ } catch (NullPointerException expected) {
+ }
+
+ // Can't create arrays of void.
+ try {
+ Object array = VMRuntime.getRuntime().newNonMovableArray(void.class, 0);
+ assertTrue(false);
+ } catch (IllegalArgumentException expected) {
+ }
+
+ int maxLengthForLoop = 16 * 1024;
+ int step = 67;
+ doTestNewNonMovableArray(boolean.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(byte.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(char.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(short.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(int.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(long.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(float.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(double.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(Object.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(Number.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(String.class, step, maxLengthForLoop);
+ doTestNewNonMovableArray(Runnable.class, step, maxLengthForLoop);
+ }
+
+ private void doTestNewUnpaddedArray(Class<?> componentType, int step, int maxLength) {
+ // Can't create negative sized arrays.
+ try {
+ Object array = VMRuntime.getRuntime().newUnpaddedArray(componentType, -1);
+ assertTrue(false);
+ } catch (NegativeArraySizeException expected) {
+ }
+
+ try {
+ Object array = VMRuntime.getRuntime().newUnpaddedArray(componentType, Integer.MIN_VALUE);
+ assertTrue(false);
+ } catch (NegativeArraySizeException expected) {
+ }
+
+ // Allocate arrays in a loop and check their properties.
+ for (int i = 0; i <= maxLength; i += step) {
+ Object array = VMRuntime.getRuntime().newUnpaddedArray(componentType, i);
+ assertTrue(array.getClass().isArray());
+ assertEquals(array.getClass().getComponentType(), componentType);
+ assertTrue(Array.getLength(array) >= i);
+ }
+ }
+
+ public void testNewUnpaddedArray() {
+ // Can't create arrays with no component type.
+ try {
+ Object array = VMRuntime.getRuntime().newUnpaddedArray(null, 0);
+ assertTrue(false);
+ } catch (NullPointerException expected) {
+ }
+
+ // Can't create arrays of void.
+ try {
+ Object array = VMRuntime.getRuntime().newUnpaddedArray(void.class, 0);
+ assertTrue(false);
+ } catch (IllegalArgumentException expected) {
+ }
+
+ int maxLengthForLoop = 16 * 1024;
+ int step = 67;
+ doTestNewUnpaddedArray(boolean.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(byte.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(char.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(short.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(int.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(long.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(float.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(double.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(Object.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(Number.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(String.class, step, maxLengthForLoop);
+ doTestNewUnpaddedArray(Runnable.class, step, maxLengthForLoop);
+ }
+}
+
diff --git a/luni/src/test/java/libcore/icu/AlphabeticIndexTest.java b/luni/src/test/java/libcore/icu/AlphabeticIndexTest.java
index 801db4b..8b8c729 100644
--- a/luni/src/test/java/libcore/icu/AlphabeticIndexTest.java
+++ b/luni/src/test/java/libcore/icu/AlphabeticIndexTest.java
@@ -51,8 +51,10 @@
// Kanji (sorts to inflow section)
assertHasLabel(ja, "\u65e5", "");
+
// http://bugs.icu-project.org/trac/ticket/10423 / http://b/10809397
assertHasLabel(ja, "\u95c7", "");
+ assertHasLabel(ja, "\u308f", "わ");
// English
assertHasLabel(ja, "Smith", "S");
@@ -106,12 +108,13 @@
}
public void test_de() throws Exception {
- // German: [A-S,Sch,St,T-Z] (no ß or umlauted characters in standard alphabet)
+ // German: [A-Z] (no ß or umlauted characters in standard alphabet)
AlphabeticIndex.ImmutableIndex de = createIndex(Locale.GERMAN);
assertHasLabel(de, "ßind", "S");
assertHasLabel(de, "Sacher", "S");
- assertHasLabel(de, "Schiller", "Sch");
- assertHasLabel(de, "Steiff", "St");
+ // "Sch" and "St" are also options for lists by last name.
+ assertHasLabel(de, "Schiller", "S");
+ assertHasLabel(de, "Steiff", "S");
}
public void test_th() throws Exception {
diff --git a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
index c8cf572..18228f9 100644
--- a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
@@ -16,13 +16,9 @@
package libcore.icu;
-import java.util.Arrays;
-import java.util.Locale;
-
import java.util.Calendar;
-import java.util.Date;
+import java.util.Locale;
import java.util.TimeZone;
-
import static libcore.icu.DateIntervalFormat.*;
public class DateIntervalFormatTest extends junit.framework.TestCase {
@@ -89,26 +85,26 @@
assertEquals("19.01.2009 - 09.02.2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("1/19/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009 – 22/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009 – 22/4/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009 – 9/2/2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/4/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–9/2/2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009 – 22/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009 – 22/4/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19/1/2009 – 9/2/2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/4/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–9/2/2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
// These are some random other test cases I came up with.
- assertEquals("January 19–22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("Jan 19–22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("January 19 – 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("Jan 19 – 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
assertEquals("Mon, Jan 19 – Thu, Jan 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("Monday, January 19 – Thursday, January 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
assertEquals("January 19 – April 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
assertEquals("Jan 19 – Apr 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
assertEquals("Mon, Jan 19 – Wed, Apr 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("January–April 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+ assertEquals("January – April 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
assertEquals("Jan 19, 2009 – Feb 9, 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
assertEquals("Jan 2009 – Feb 2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
@@ -135,36 +131,36 @@
// The same tests but for es_US.
assertEquals("19–22 enero 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19–22 ene 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun 19 ene – jue 22 ene 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes 19 enero – jueves 22 enero 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19–22 ene. 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene.–jue., 22 ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 enero–jueves, 22 enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 enero – 22 abril 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 ene – 22 abr 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun 19 ene – mié 22 abr 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("enero–abril 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+ assertEquals("19 enero–22 abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 ene.–22 abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene.–mié., 22 abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("enero–abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 ene 2009 – 9 feb 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("ene 2009 – feb 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 enero 2009 – 9 febrero 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes 19 enero 2009 – jueves 9 febrero 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19 ene. de 2009–9 feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("ene. 2009–feb. 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 enero de 2009–9 febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 enero de 2009–jueves, 9 febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
// The same tests but for es_ES.
assertEquals("19–22 enero 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19–22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun 19 ene – jue 22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes 19 enero – jueves 22 enero 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene.–jue., 22 ene. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 enero–jueves, 22 enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 enero – 22 abril 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 ene – 22 abr 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun 19 ene – mié 22 abr 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("enero–abril 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+ assertEquals("19 enero–22 abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 ene.–22 abr. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene.–mié., 22 abr. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("enero–abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 ene 2009 – 9 feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("ene 2009 – feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 enero 2009 – 9 febrero 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes 19 enero 2009 – jueves 9 febrero 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19 ene. de 2009–9 feb. de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("ene. 2009–feb. 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 enero de 2009–9 febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 enero de 2009–jueves, 9 febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
}
// http://b/8862241 - we should be able to format dates past 2038.
@@ -221,11 +217,11 @@
assertEquals("4 PM", formatDateRange(l, utc, teaTime, teaTime, abbr12));
// Abbreviated on-the-hour ranges.
- assertEquals("00:00–16:00", formatDateRange(l, utc, midnight, teaTime, abbr24));
+ assertEquals("00:00 – 16:00", formatDateRange(l, utc, midnight, teaTime, abbr24));
assertEquals("12 AM – 4 PM", formatDateRange(l, utc, midnight, teaTime, abbr12));
// Abbreviated mixed ranges.
- assertEquals("00:00–16:01", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr24));
+ assertEquals("00:00 – 16:01", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr24));
assertEquals("12:00 AM – 4:01 PM", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr12));
}
@@ -261,7 +257,7 @@
int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
- assertEquals("January 1, 1970, 22:00–00:00", formatDateRange(l, utc, 22 * HOUR, 24 * HOUR, flags));
+ assertEquals("January 1, 1970, 22:00 – 00:00", formatDateRange(l, utc, 22 * HOUR, 24 * HOUR, flags));
assertEquals("January 1, 1970, 22:00 – January 2, 1970, 00:30", formatDateRange(l, utc, 22 * HOUR, 24 * HOUR + 30 * MINUTE, flags));
}
@@ -361,9 +357,9 @@
// the Gregorian calendar, we want to deliberately force icu4c to agree, otherwise we'd have
// a mix of calendars throughout an app's UI depending on whether Java or native code formatted
// the date.
- //assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه.ش.", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
- //assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
- //assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
+ // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه.ش.", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
+ // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
+ // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
// For now, here are the localized Gregorian strings instead...
assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
diff --git a/luni/src/test/java/libcore/icu/LocaleDataTest.java b/luni/src/test/java/libcore/icu/LocaleDataTest.java
index 27eda86..559744c 100644
--- a/luni/src/test/java/libcore/icu/LocaleDataTest.java
+++ b/luni/src/test/java/libcore/icu/LocaleDataTest.java
@@ -48,9 +48,9 @@
assertEquals("Sun", l.shortStandAloneWeekdayNames[1]);
assertEquals("S", l.tinyStandAloneWeekdayNames[1]);
- assertEquals("Yesterday", l.yesterday);
- assertEquals("Today", l.today);
- assertEquals("Tomorrow", l.tomorrow);
+ assertEquals("yesterday", l.yesterday);
+ assertEquals("today", l.today);
+ assertEquals("tomorrow", l.tomorrow);
}
public void test_de_DE() throws Exception {
diff --git a/luni/src/test/java/libcore/icu/NativePluralRulesTest.java b/luni/src/test/java/libcore/icu/NativePluralRulesTest.java
index 73699ff..703a94a 100644
--- a/luni/src/test/java/libcore/icu/NativePluralRulesTest.java
+++ b/luni/src/test/java/libcore/icu/NativePluralRulesTest.java
@@ -21,7 +21,6 @@
public class NativePluralRulesTest extends junit.framework.TestCase {
public void testEnglish() throws Exception {
NativePluralRules npr = NativePluralRules.forLocale(new Locale("en", "US"));
- assertEquals(NativePluralRules.OTHER, npr.quantityForInt(-1));
assertEquals(NativePluralRules.OTHER, npr.quantityForInt(0));
assertEquals(NativePluralRules.ONE, npr.quantityForInt(1));
assertEquals(NativePluralRules.OTHER, npr.quantityForInt(2));
@@ -29,7 +28,6 @@
public void testCzech() throws Exception {
NativePluralRules npr = NativePluralRules.forLocale(new Locale("cs", "CZ"));
- assertEquals(NativePluralRules.OTHER, npr.quantityForInt(-1));
assertEquals(NativePluralRules.OTHER, npr.quantityForInt(0));
assertEquals(NativePluralRules.ONE, npr.quantityForInt(1));
assertEquals(NativePluralRules.FEW, npr.quantityForInt(2));
@@ -40,7 +38,6 @@
public void testArabic() throws Exception {
NativePluralRules npr = NativePluralRules.forLocale(new Locale("ar"));
- assertEquals(NativePluralRules.OTHER, npr.quantityForInt(-1));
assertEquals(NativePluralRules.ZERO, npr.quantityForInt(0));
assertEquals(NativePluralRules.ONE, npr.quantityForInt(1));
assertEquals(NativePluralRules.TWO, npr.quantityForInt(2));
@@ -62,6 +59,7 @@
assertEquals(NativePluralRules.ONE, he.quantityForInt(1));
assertEquals(NativePluralRules.TWO, he.quantityForInt(2));
assertEquals(NativePluralRules.OTHER, he.quantityForInt(3));
- assertEquals(NativePluralRules.MANY, he.quantityForInt(10));
+ assertEquals(NativePluralRules.OTHER, he.quantityForInt(10));
}
}
+
diff --git a/luni/src/test/java/libcore/io/MemoryTest.java b/luni/src/test/java/libcore/io/MemoryTest.java
index 9a596fb..c817b20 100644
--- a/luni/src/test/java/libcore/io/MemoryTest.java
+++ b/luni/src/test/java/libcore/io/MemoryTest.java
@@ -32,10 +32,10 @@
int scale = SizeOf.INT;
VMRuntime runtime = VMRuntime.getRuntime();
byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, scale * values.length + 1);
- int base_ptr = (int) runtime.addressOf(array);
+ long base_ptr = runtime.addressOf(array);
for (int ptr_offset = 0; ptr_offset < 2; ++ptr_offset) {
- int ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
+ long ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
Arrays.fill(array, (byte) 0);
// Regular copy.
@@ -57,7 +57,7 @@
}
}
- private void assertIntsEqual(int[] expectedValues, int ptr, boolean swap) {
+ private void assertIntsEqual(int[] expectedValues, long ptr, boolean swap) {
for (int i = 0; i < expectedValues.length; ++i) {
assertEquals(expectedValues[i], Memory.peekInt(ptr + SizeOf.INT * i, swap));
}
@@ -73,10 +73,10 @@
int scale = SizeOf.LONG;
VMRuntime runtime = VMRuntime.getRuntime();
byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, scale * values.length + 1);
- int base_ptr = (int) runtime.addressOf(array);
+ long base_ptr = runtime.addressOf(array);
for (int ptr_offset = 0; ptr_offset < 2; ++ptr_offset) {
- int ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
+ long ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
Arrays.fill(array, (byte) 0);
// Regular copy.
@@ -98,7 +98,7 @@
}
}
- private void assertLongsEqual(long[] expectedValues, int ptr, boolean swap) {
+ private void assertLongsEqual(long[] expectedValues, long ptr, boolean swap) {
for (int i = 0; i < expectedValues.length; ++i) {
assertEquals(expectedValues[i], Memory.peekLong(ptr + SizeOf.LONG * i, swap));
}
@@ -111,10 +111,10 @@
int scale = SizeOf.SHORT;
VMRuntime runtime = VMRuntime.getRuntime();
byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, scale * values.length + 1);
- int base_ptr = (int) runtime.addressOf(array);
+ long base_ptr = runtime.addressOf(array);
for (int ptr_offset = 0; ptr_offset < 2; ++ptr_offset) {
- int ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
+ long ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
Arrays.fill(array, (byte) 0);
// Regular copy.
@@ -136,7 +136,7 @@
}
}
- private void assertShortsEqual(short[] expectedValues, int ptr, boolean swap) {
+ private void assertShortsEqual(short[] expectedValues, long ptr, boolean swap) {
for (int i = 0; i < expectedValues.length; ++i) {
assertEquals(expectedValues[i], Memory.peekShort(ptr + SizeOf.SHORT * i, swap));
}
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index 624c119..cf28122 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -16,6 +16,7 @@
package libcore.io;
+import android.system.StructUcred;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -26,8 +27,7 @@
import java.net.SocketAddress;
import java.util.Locale;
import junit.framework.TestCase;
-
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
public class OsTest extends TestCase {
public void testIsSocket() throws Exception {
diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java
index 4135ae4..b4101f9 100644
--- a/luni/src/test/java/libcore/java/io/FileTest.java
+++ b/luni/src/test/java/libcore/java/io/FileTest.java
@@ -217,8 +217,12 @@
public void test_getAbsolutePath() throws Exception {
String userDir = System.getProperty("user.dir");
+ if (!userDir.endsWith(File.separator)) {
+ userDir = userDir + File.separator;
+ }
+
File f = new File("poop");
- assertEquals(userDir + File.separator + "poop", f.getAbsolutePath());
+ assertEquals(userDir + "poop", f.getAbsolutePath());
}
public void test_getSpace() throws Exception {
diff --git a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
index e5fd39f..30ae7eb 100755
--- a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
@@ -207,7 +207,10 @@
private static void confirmInterrupted(Thread thread) throws InterruptedException {
// validate and clear interrupted bit before join
- assertTrue(Thread.interrupted());
- thread.join();
+ try {
+ assertTrue(Thread.interrupted());
+ } finally {
+ thread.join();
+ }
}
}
diff --git a/luni/src/test/java/libcore/java/lang/IntegerTest.java b/luni/src/test/java/libcore/java/lang/IntegerTest.java
index 2d8c082..b54b322 100644
--- a/luni/src/test/java/libcore/java/lang/IntegerTest.java
+++ b/luni/src/test/java/libcore/java/lang/IntegerTest.java
@@ -16,19 +16,118 @@
package libcore.java.lang;
+import java.util.Properties;
+
public class IntegerTest extends junit.framework.TestCase {
- public void test_compare() throws Exception {
- final int min = Integer.MIN_VALUE;
- final int zero = 0;
- final int max = Integer.MAX_VALUE;
- assertTrue(Integer.compare(max, max) == 0);
- assertTrue(Integer.compare(min, min) == 0);
- assertTrue(Integer.compare(zero, zero) == 0);
- assertTrue(Integer.compare(max, zero) > 0);
- assertTrue(Integer.compare(max, min) > 0);
- assertTrue(Integer.compare(zero, max) < 0);
- assertTrue(Integer.compare(zero, min) > 0);
- assertTrue(Integer.compare(min, zero) < 0);
- assertTrue(Integer.compare(min, max) < 0);
+
+ public void testSystemProperties() {
+ Properties originalProperties = System.getProperties();
+ try {
+ Properties testProperties = new Properties();
+ testProperties.put("testIncInt", "notInt");
+ System.setProperties(testProperties);
+ assertNull("returned incorrect default Integer",
+ Integer.getInteger("testIncInt"));
+ assertEquals(new Integer(4), Integer.getInteger("testIncInt", 4));
+ assertEquals(new Integer(4),
+ Integer.getInteger("testIncInt", new Integer(4)));
+ } finally {
+ System.setProperties(originalProperties);
}
+ }
+
+ public void testCompare() throws Exception {
+ final int min = Integer.MIN_VALUE;
+ final int zero = 0;
+ final int max = Integer.MAX_VALUE;
+ assertTrue(Integer.compare(max, max) == 0);
+ assertTrue(Integer.compare(min, min) == 0);
+ assertTrue(Integer.compare(zero, zero) == 0);
+ assertTrue(Integer.compare(max, zero) > 0);
+ assertTrue(Integer.compare(max, min) > 0);
+ assertTrue(Integer.compare(zero, max) < 0);
+ assertTrue(Integer.compare(zero, min) > 0);
+ assertTrue(Integer.compare(min, zero) < 0);
+ assertTrue(Integer.compare(min, max) < 0);
+ }
+
+ public void testParseInt() throws Exception {
+ assertEquals(0, Integer.parseInt("+0", 10));
+ assertEquals(473, Integer.parseInt("+473", 10));
+ assertEquals(255, Integer.parseInt("+FF", 16));
+ assertEquals(102, Integer.parseInt("+1100110", 2));
+ assertEquals(2147483647, Integer.parseInt("+2147483647", 10));
+ assertEquals(411787, Integer.parseInt("Kona", 27));
+ assertEquals(411787, Integer.parseInt("+Kona", 27));
+ assertEquals(-145, Integer.parseInt("-145", 10));
+
+ try {
+ Integer.parseInt("--1", 10); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Integer.parseInt("++1", 10); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Integer.parseInt("Kona", 10); // base too small
+ fail();
+ } catch (NumberFormatException expected) {}
+ }
+
+ public void testDecodeInt() throws Exception {
+ assertEquals(0, Integer.decode("+0").intValue());
+ assertEquals(473, Integer.decode("+473").intValue());
+ assertEquals(255, Integer.decode("+0xFF").intValue());
+ assertEquals(16, Integer.decode("+020").intValue());
+ assertEquals(2147483647, Integer.decode("+2147483647").intValue());
+ assertEquals(-73, Integer.decode("-73").intValue());
+ assertEquals(-255, Integer.decode("-0xFF").intValue());
+ assertEquals(255, Integer.decode("+#FF").intValue());
+ assertEquals(-255, Integer.decode("-#FF").intValue());
+
+ try {
+ Integer.decode("--1"); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Integer.decode("++1"); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Integer.decode("-+1"); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Integer.decode("Kona"); // invalid number
+ fail();
+ } catch (NumberFormatException expected) {}
+ }
+
+ public void testParsePositiveInt() throws Exception {
+ assertEquals(0, Integer.parsePositiveInt("0", 10));
+ assertEquals(473, Integer.parsePositiveInt("473", 10));
+ assertEquals(255, Integer.parsePositiveInt("FF", 16));
+
+ try {
+ Integer.parsePositiveInt("-1", 10);
+ fail();
+ } catch (NumberFormatException e) {}
+
+ try {
+ Integer.parsePositiveInt("+1", 10);
+ fail();
+ } catch (NumberFormatException e) {}
+
+ try {
+ Integer.parsePositiveInt("+0", 16);
+ fail();
+ } catch (NumberFormatException e) {}
+ }
+
}
diff --git a/luni/src/test/java/libcore/java/lang/LongTest.java b/luni/src/test/java/libcore/java/lang/LongTest.java
index 9e143da..0d1741a 100644
--- a/luni/src/test/java/libcore/java/lang/LongTest.java
+++ b/luni/src/test/java/libcore/java/lang/LongTest.java
@@ -16,8 +16,25 @@
package libcore.java.lang;
+import java.util.Properties;
+
public class LongTest extends junit.framework.TestCase {
- public void test_compare() throws Exception {
+
+ public void testSystemProperties() {
+ Properties originalProperties = System.getProperties();
+ try {
+ Properties testProperties = new Properties();
+ testProperties.put("testIncLong", "string");
+ System.setProperties(testProperties);
+ assertNull(Long.getLong("testIncLong"));
+ assertEquals(new Long(4), Long.getLong("testIncLong", 4L));
+ assertEquals(new Long(4), Long.getLong("testIncLong", new Long(4)));
+ } finally {
+ System.setProperties(originalProperties);
+ }
+ }
+
+ public void testCompare() throws Exception {
final long min = Long.MIN_VALUE;
final long zero = 0L;
final long max = Long.MAX_VALUE;
@@ -32,11 +49,91 @@
assertTrue(Long.compare(min, max) < 0);
}
- public void test_signum() throws Exception {
+ public void testSignum() throws Exception {
assertEquals(0, Long.signum(0));
assertEquals(1, Long.signum(1));
assertEquals(-1, Long.signum(-1));
assertEquals(1, Long.signum(Long.MAX_VALUE));
assertEquals(-1, Long.signum(Long.MIN_VALUE));
}
+
+ public void testParsePositiveLong() throws Exception {
+ assertEquals(0, Long.parsePositiveLong("0", 10));
+ assertEquals(473, Long.parsePositiveLong("473", 10));
+ assertEquals(255, Long.parsePositiveLong("FF", 16));
+
+ try {
+ Long.parsePositiveLong("-1", 10);
+ fail();
+ } catch (NumberFormatException e) {}
+
+ try {
+ Long.parsePositiveLong("+1", 10);
+ fail();
+ } catch (NumberFormatException e) {}
+
+ try {
+ Long.parsePositiveLong("+0", 16);
+ fail();
+ } catch (NumberFormatException e) {}
+ }
+
+ public void testParseLong() throws Exception {
+ assertEquals(0, Long.parseLong("+0", 10));
+ assertEquals(473, Long.parseLong("+473", 10));
+ assertEquals(255, Long.parseLong("+FF", 16));
+ assertEquals(102, Long.parseLong("+1100110", 2));
+ assertEquals(Long.MAX_VALUE, Long.parseLong("+" + Long.MAX_VALUE, 10));
+ assertEquals(411787, Long.parseLong("Kona", 27));
+ assertEquals(411787, Long.parseLong("+Kona", 27));
+ assertEquals(-145, Long.parseLong("-145", 10));
+
+ try {
+ Long.parseLong("--1", 10); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Long.parseLong("++1", 10); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Long.parseLong("Kona", 10); // base to small
+ fail();
+ } catch (NumberFormatException expected) {}
+ }
+
+ public void testDecodeLong() throws Exception {
+ assertEquals(0, Long.decode("+0").longValue());
+ assertEquals(473, Long.decode("+473").longValue());
+ assertEquals(255, Long.decode("+0xFF").longValue());
+ assertEquals(16, Long.decode("+020").longValue());
+ assertEquals(Long.MAX_VALUE, Long.decode("+" + Long.MAX_VALUE).longValue());
+ assertEquals(-73, Long.decode("-73").longValue());
+ assertEquals(-255, Long.decode("-0xFF").longValue());
+ assertEquals(255, Long.decode("+#FF").longValue());
+ assertEquals(-255, Long.decode("-#FF").longValue());
+
+ try {
+ Long.decode("--1"); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Long.decode("++1"); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Long.decode("+-1"); // multiple sign chars
+ fail();
+ } catch (NumberFormatException expected) {}
+
+ try {
+ Long.decode("Kona"); // invalid number
+ fail();
+ } catch (NumberFormatException expected) {}
+ }
+
}
diff --git a/luni/src/test/java/libcore/java/lang/OldAndroidParseIntTest.java b/luni/src/test/java/libcore/java/lang/OldAndroidParseIntTest.java
deleted file mode 100644
index 08351d6..0000000
--- a/luni/src/test/java/libcore/java/lang/OldAndroidParseIntTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2008 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.java.lang;
-
-import junit.framework.TestCase;
-
-/**
- * Tests for functionality of class Integer to parse integers.
- */
-public class OldAndroidParseIntTest extends TestCase {
-
- public void testParseInt() throws Exception {
- assertEquals(0, Integer.parseInt("0", 10));
- assertEquals(473, Integer.parseInt("473", 10));
- assertEquals(0, Integer.parseInt("-0", 10));
- assertEquals(-255, Integer.parseInt("-FF", 16));
- assertEquals(102, Integer.parseInt("1100110", 2));
- assertEquals(2147483647, Integer.parseInt("2147483647", 10));
- assertEquals(-2147483648, Integer.parseInt("-2147483648", 10));
-
- try {
- Integer.parseInt("2147483648", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- try {
- Integer.parseInt("-2147483649", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- // One digit too many
- try {
- Integer.parseInt("21474836470", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- try {
- Integer.parseInt("-21474836480", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- try {
- Integer.parseInt("21474836471", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- try {
- Integer.parseInt("-21474836481", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- try {
- Integer.parseInt("214748364710", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- try {
- Integer.parseInt("-214748364811", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- try {
- Integer.parseInt("99", 8);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- try {
- Integer.parseInt("Kona", 10);
- fail();
- } catch (NumberFormatException e) {
- // ok
- }
-
- assertEquals(411787, Integer.parseInt("Kona", 27));
- }
-}
diff --git a/luni/src/test/java/libcore/java/lang/OldIntegerTest.java b/luni/src/test/java/libcore/java/lang/OldIntegerTest.java
deleted file mode 100644
index 462ee19..0000000
--- a/luni/src/test/java/libcore/java/lang/OldIntegerTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.java.lang;
-
-import java.util.Properties;
-import junit.framework.TestCase;
-
-public class OldIntegerTest extends TestCase {
- private Properties orgProps;
-
- @Override
- protected void setUp() {
- orgProps = System.getProperties();
- }
-
- @Override
- protected void tearDown() {
- System.setProperties(orgProps);
- }
-
- public void test_getIntegerLjava_lang_StringI() {
- // Test for method java.lang.Integer
- // java.lang.Integer.getInteger(java.lang.String, int)
- Properties tProps = new Properties();
- tProps.put("testIncInt", "notInt");
- System.setProperties(tProps);
- assertTrue("returned incorrect default Integer", Integer.getInteger(
- "testIncInt", 4).equals(new Integer(4)));
- }
-
- public void test_getIntegerLjava_lang_StringLjava_lang_Integer() {
- // Test for method java.lang.Integer
- // java.lang.Integer.getInteger(java.lang.String, java.lang.Integer)
- Properties tProps = new Properties();
- tProps.put("testIncInt", "notInt");
- System.setProperties(tProps);
- assertTrue("returned incorrect default Integer", Integer.getInteger(
- "testIncInt", new Integer(4)).equals(new Integer(4)));
- }
-
- public void test_intValue() {
- assertEquals(Integer.MAX_VALUE, new Integer(Integer.MAX_VALUE).intValue());
- assertEquals(Integer.MIN_VALUE, new Integer(Integer.MIN_VALUE).intValue());
- }
-
- public void test_longValue() {
- assertEquals(Integer.MAX_VALUE, new Integer(Integer.MAX_VALUE).longValue());
- assertEquals(Integer.MIN_VALUE, new Integer(Integer.MIN_VALUE).longValue());
- }
-
- public void test_shortValue() {
- assertEquals(-1, new Integer(Integer.MAX_VALUE).shortValue());
- assertEquals(0, new Integer(Integer.MIN_VALUE).shortValue());
- }
-}
diff --git a/luni/src/test/java/libcore/java/lang/OldLongTest.java b/luni/src/test/java/libcore/java/lang/OldLongTest.java
deleted file mode 100644
index 6fb7d49..0000000
--- a/luni/src/test/java/libcore/java/lang/OldLongTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.java.lang;
-
-import java.util.Properties;
-import junit.framework.TestCase;
-
-public class OldLongTest extends TestCase {
- private Properties orgProps;
-
- @Override
- protected void setUp() {
- orgProps = System.getProperties();
- }
-
- @Override
- protected void tearDown() {
- System.setProperties(orgProps);
- }
-
- public void test_getLongLjava_lang_String() {
- Properties tProps = new Properties();
- tProps.put("testLong", "99");
- tProps.put("testIncLong", "string");
- System.setProperties(tProps);
- assertNull("returned incorrect default Long",
- Long.getLong("testIncLong"));
- }
-
- public void test_getLongLjava_lang_StringJ() {
- // Test for method java.lang.Long
- // java.lang.Long.getLong(java.lang.String, long)
- Properties tProps = new Properties();
- tProps.put("testIncLong", "string");
- System.setProperties(tProps);
- assertTrue("returned incorrect default Long", Long.getLong("testIncLong", 4L)
- .equals(new Long(4)));
- }
-
- public void test_getLongLjava_lang_StringLjava_lang_Long() {
- // Test for method java.lang.Long
- // java.lang.Long.getLong(java.lang.String, java.lang.Long)
- Properties tProps = new Properties();
- tProps.put("testIncLong", "string");
- System.setProperties(tProps);
- assertTrue("returned incorrect default Long", Long.getLong("testIncLong",
- new Long(4)).equals(new Long(4)));
- }
-
- public void test_floatValue() {
- assertEquals(Long.MAX_VALUE, new Long(Long.MAX_VALUE).floatValue(), 0F);
- assertEquals(Long.MIN_VALUE, new Long(Long.MIN_VALUE).floatValue(), 0F);
- }
-
- public void test_intValue() {
- assertEquals(-1, new Long(Long.MAX_VALUE).intValue());
- assertEquals(0, new Long(Long.MIN_VALUE).intValue());
- }
-
- public void test_longValue() {
- assertEquals(Long.MAX_VALUE, new Long(Long.MAX_VALUE).longValue());
- assertEquals(Long.MIN_VALUE, new Long(Long.MIN_VALUE).longValue());
- }
-
- public void test_shortValue() {
- assertEquals(-1, new Long(Long.MAX_VALUE).shortValue());
- assertEquals(0, new Long(Long.MIN_VALUE).shortValue());
- }
-}
diff --git a/luni/src/test/java/libcore/java/lang/StringTest.java b/luni/src/test/java/libcore/java/lang/StringTest.java
index 0c0e353..7df852e 100644
--- a/luni/src/test/java/libcore/java/lang/StringTest.java
+++ b/luni/src/test/java/libcore/java/lang/StringTest.java
@@ -174,7 +174,7 @@
/**
* Tests a widely assumed performance characteristic of String.substring():
- * that it reuses the original's backing array. Although behaviour should be
+ * that it reuses the original's backing array. Although behavior should be
* correct even if this test fails, many applications may suffer
* significant performance degradation.
*/
@@ -187,7 +187,7 @@
/**
* Tests a widely assumed performance characteristic of string's copy
* constructor: that it ensures the backing array is the same length as the
- * string. Although behaviour should be correct even if this test fails,
+ * string. Although behavior should be correct even if this test fails,
* many applications may suffer significant performance degradation.
*/
public void testStringCopiesAvoidHeapRetention() throws IllegalAccessException {
@@ -243,33 +243,43 @@
};
public void testCaseMapping_tr_TR() {
- Locale trTR = new Locale("tr", "TR");
- assertEquals(LATIN_SMALL_I, LATIN_SMALL_I.toLowerCase(trTR));
- assertEquals(LATIN_SMALL_I, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toLowerCase(trTR));
- assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_SMALL_DOTLESS_I.toLowerCase(trTR));
+ Locale tr_TR = new Locale("tr", "TR");
+ assertEquals(LATIN_SMALL_I, LATIN_SMALL_I.toLowerCase(tr_TR));
+ assertEquals(LATIN_SMALL_I, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toLowerCase(tr_TR));
+ assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_SMALL_DOTLESS_I.toLowerCase(tr_TR));
- assertEquals(LATIN_CAPITAL_I, LATIN_CAPITAL_I.toUpperCase(trTR));
- assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toUpperCase(trTR));
- assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_SMALL_I.toUpperCase(trTR));
+ assertEquals(LATIN_CAPITAL_I, LATIN_CAPITAL_I.toUpperCase(tr_TR));
+ assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toUpperCase(tr_TR));
+ assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_SMALL_I.toUpperCase(tr_TR));
- assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_DOTLESS_I.toUpperCase(trTR));
- assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_CAPITAL_I.toLowerCase(trTR));
+ assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_DOTLESS_I.toUpperCase(tr_TR));
+ assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_CAPITAL_I.toLowerCase(tr_TR));
}
public void testCaseMapping_en_US() {
- Locale enUs = new Locale("en", "US");
- assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_I.toUpperCase(enUs));
- assertEquals(LATIN_CAPITAL_I, LATIN_CAPITAL_I.toUpperCase(enUs));
- assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toUpperCase(enUs));
+ Locale en_US = new Locale("en", "US");
+ assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_I.toUpperCase(en_US));
+ assertEquals(LATIN_CAPITAL_I, LATIN_CAPITAL_I.toUpperCase(en_US));
+ assertEquals(LATIN_CAPITAL_I_WITH_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toUpperCase(en_US));
- assertEquals(LATIN_SMALL_I, LATIN_SMALL_I.toLowerCase(enUs));
- assertEquals(LATIN_SMALL_I, LATIN_CAPITAL_I.toLowerCase(enUs));
- assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_SMALL_DOTLESS_I.toLowerCase(enUs));
+ assertEquals(LATIN_SMALL_I, LATIN_SMALL_I.toLowerCase(en_US));
+ assertEquals(LATIN_SMALL_I, LATIN_CAPITAL_I.toLowerCase(en_US));
+ assertEquals(LATIN_SMALL_DOTLESS_I, LATIN_SMALL_DOTLESS_I.toLowerCase(en_US));
- assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_DOTLESS_I.toUpperCase(enUs));
+ assertEquals(LATIN_CAPITAL_I, LATIN_SMALL_DOTLESS_I.toUpperCase(en_US));
// http://b/3325799: the RI fails this because it's using an obsolete version of the Unicode rules.
// Android correctly preserves canonical equivalence. (See the separate test for tr_TR.)
- assertEquals(LATIN_SMALL_I + COMBINING_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toLowerCase(enUs));
+ assertEquals(LATIN_SMALL_I + COMBINING_DOT_ABOVE, LATIN_CAPITAL_I_WITH_DOT_ABOVE.toLowerCase(en_US));
+ }
+
+ public void testCaseMapping_el() {
+ Locale el_GR = new Locale("el", "GR");
+ assertEquals("ΟΔΟΣ ΟΔΟΣ ΣΟ ΣΟ OΣ ΟΣ Σ ΕΞ", "ΟΔΌΣ Οδός Σο ΣΟ oΣ ΟΣ σ ἕξ".toUpperCase(el_GR));
+ assertEquals("ΟΔΟΣ ΟΔΟΣ ΣΟ ΣΟ OΣ ΟΣ Σ ΕΞ", "ΟΔΌΣ Οδός Σο ΣΟ oΣ ΟΣ σ ἕξ".toUpperCase(el_GR));
+ assertEquals("ΟΔΟΣ ΟΔΟΣ ΣΟ ΣΟ OΣ ΟΣ Σ ΕΞ", "ΟΔΌΣ Οδός Σο ΣΟ oΣ ΟΣ σ ἕξ".toUpperCase(el_GR));
+
+ Locale en_US = new Locale("en", "US");
+ assertEquals("ΟΔΌΣ ΟΔΌΣ ΣΟ ΣΟ OΣ ΟΣ Σ ἝΞ", "ΟΔΌΣ Οδός Σο ΣΟ oΣ ΟΣ σ ἕξ".toUpperCase(en_US));
}
public void testEqualsIgnoreCase_tr_TR() {
diff --git a/luni/src/test/java/libcore/java/lang/ThreadTest.java b/luni/src/test/java/libcore/java/lang/ThreadTest.java
index 68ad795..8545a20 100644
--- a/luni/src/test/java/libcore/java/lang/ThreadTest.java
+++ b/luni/src/test/java/libcore/java/lang/ThreadTest.java
@@ -59,15 +59,46 @@
}
public void testThreadSleep() throws Exception {
- int millis = 1000;
- long start = System.currentTimeMillis();
+ int millis = 1000;
+ long start = System.currentTimeMillis();
- Thread.sleep(millis);
+ Thread.sleep(millis);
- long elapsed = System.currentTimeMillis() - start;
- long offBy = Math.abs(elapsed - millis);
+ long elapsed = System.currentTimeMillis() - start;
+ long offBy = Math.abs(elapsed - millis);
- assertTrue("Actual sleep off by " + offBy + " ms", offBy <= 250);
+ assertTrue("Actual sleep off by " + offBy + " ms", offBy <= 250);
+ }
+
+ public void testThreadInterrupted() throws Exception {
+ Thread.currentThread().interrupt();
+ try {
+ Thread.sleep(0);
+ fail();
+ } catch (InterruptedException e) {
+ assertFalse(Thread.currentThread().isInterrupted());
+ }
+ }
+
+ public void testThreadSleepIllegalArguments() throws Exception {
+
+ try {
+ Thread.sleep(-1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Thread.sleep(0, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Thread.sleep(0, 1000000);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
}
public void testThreadWakeup() throws Exception {
diff --git a/luni/src/test/java/libcore/java/math/BigIntegerTest.java b/luni/src/test/java/libcore/java/math/BigIntegerTest.java
index 15a0dad..58c68a1 100644
--- a/luni/src/test/java/libcore/java/math/BigIntegerTest.java
+++ b/luni/src/test/java/libcore/java/math/BigIntegerTest.java
@@ -156,4 +156,32 @@
assertTrue(b.isProbablePrime(100));
}
}
+
+ public void test_negativeValues_superfluousZeros() throws Exception {
+ byte[] trimmedBytes = new byte[] {
+ (byte) 0xae, (byte) 0x0f, (byte) 0xa1, (byte) 0x93
+ };
+ byte[] extraZeroesBytes = new byte[] {
+ (byte) 0xff, (byte) 0xae, (byte) 0x0f, (byte) 0xa1, (byte) 0x93
+ };
+
+ BigInteger trimmed = new BigInteger(trimmedBytes);
+ BigInteger extraZeroes = new BigInteger(extraZeroesBytes);
+
+ assertEquals(trimmed, extraZeroes);
+ }
+
+ public void test_positiveValues_superfluousZeros() throws Exception {
+ byte[] trimmedBytes = new byte[] {
+ (byte) 0x2e, (byte) 0x0f, (byte) 0xa1, (byte) 0x93
+ };
+ byte[] extraZeroesBytes = new byte[] {
+ (byte) 0x00, (byte) 0x2e, (byte) 0x0f, (byte) 0xa1, (byte) 0x93
+ };
+
+ BigInteger trimmed = new BigInteger(trimmedBytes);
+ BigInteger extraZeroes = new BigInteger(extraZeroesBytes);
+
+ assertEquals(trimmed, extraZeroes);
+ }
}
diff --git a/luni/src/test/java/libcore/java/net/DatagramSocketTest.java b/luni/src/test/java/libcore/java/net/DatagramSocketTest.java
new file mode 100644
index 0000000..86e47ec
--- /dev/null
+++ b/luni/src/test/java/libcore/java/net/DatagramSocketTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 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.java.net;
+
+import junit.framework.TestCase;
+
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+
+public class DatagramSocketTest extends TestCase {
+
+ public void testInitialState() throws Exception {
+ DatagramSocket ds = new DatagramSocket();
+ try {
+ assertTrue(ds.isBound());
+ assertTrue(ds.getBroadcast()); // The RI starts DatagramSocket in broadcast mode.
+ assertFalse(ds.isClosed());
+ assertFalse(ds.isConnected());
+ assertTrue(ds.getLocalPort() > 0);
+ assertTrue(ds.getLocalAddress().isAnyLocalAddress());
+ InetSocketAddress socketAddress = (InetSocketAddress) ds.getLocalSocketAddress();
+ assertEquals(ds.getLocalPort(), socketAddress.getPort());
+ assertEquals(ds.getLocalAddress(), socketAddress.getAddress());
+ assertNull(ds.getInetAddress());
+ assertEquals(-1, ds.getPort());
+ assertNull(ds.getRemoteSocketAddress());
+ assertFalse(ds.getReuseAddress());
+ assertNull(ds.getChannel());
+ } finally {
+ ds.close();
+ }
+ }
+
+ public void testStateAfterClose() throws Exception {
+ DatagramSocket ds = new DatagramSocket();
+ ds.close();
+ assertTrue(ds.isBound());
+ assertTrue(ds.isClosed());
+ assertFalse(ds.isConnected());
+ assertNull(ds.getLocalAddress());
+ assertEquals(-1, ds.getLocalPort());
+ assertNull(ds.getLocalSocketAddress());
+ }
+}
diff --git a/luni/src/test/java/libcore/java/net/ServerSocketTest.java b/luni/src/test/java/libcore/java/net/ServerSocketTest.java
index fe9d423..d82e934 100644
--- a/luni/src/test/java/libcore/java/net/ServerSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/ServerSocketTest.java
@@ -17,6 +17,8 @@
package libcore.java.net;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
@@ -43,4 +45,34 @@
t.join();
assertEquals(0, result[0].getSoTimeout());
}
+
+ public void testInitialState() throws Exception {
+ ServerSocket ss = new ServerSocket();
+ try {
+ assertFalse(ss.isBound());
+ assertFalse(ss.isClosed());
+ assertEquals(-1, ss.getLocalPort());
+ assertNull(ss.getLocalSocketAddress());
+ assertNull(ss.getInetAddress());
+ assertTrue(ss.getReuseAddress());
+ assertNull(ss.getChannel());
+ } finally {
+ ss.close();
+ }
+ }
+
+ public void testStateAfterClose() throws Exception {
+ ServerSocket ss = new ServerSocket();
+ ss.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 0));
+ InetSocketAddress boundAddress = (InetSocketAddress) ss.getLocalSocketAddress();
+ ss.close();
+
+ assertTrue(ss.isBound());
+ assertTrue(ss.isClosed());
+ assertEquals(boundAddress.getAddress(), ss.getInetAddress());
+ assertEquals(boundAddress.getPort(), ss.getLocalPort());
+
+ InetSocketAddress localAddressAfterClose = (InetSocketAddress) ss.getLocalSocketAddress();
+ assertEquals(boundAddress, localAddressAfterClose);
+ }
}
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index 42b7250..b9ed99c 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -20,6 +20,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
@@ -89,7 +90,7 @@
// Open a local server port.
ServerSocketChannel ssc = ServerSocketChannel.open();
InetSocketAddress listenAddr = new InetSocketAddress(host, 0);
- ssc.socket().bind(listenAddr, 0);
+ ssc.bind(listenAddr, 0);
ServerSocket ss = ssc.socket();
// Open a socket to the local port.
@@ -109,10 +110,12 @@
in.socket().setTcpNoDelay(false);
}
+ InetSocketAddress listenAddress = (InetSocketAddress) in.getLocalAddress();
InetSocketAddress outRemoteAddress = (InetSocketAddress) out.socket().getRemoteSocketAddress();
InetSocketAddress outLocalAddress = (InetSocketAddress) out.socket().getLocalSocketAddress();
InetSocketAddress inLocalAddress = (InetSocketAddress) in.socket().getLocalSocketAddress();
InetSocketAddress inRemoteAddress = (InetSocketAddress) in.socket().getRemoteSocketAddress();
+ System.err.println("listenAddress: " + listenAddr);
System.err.println("inLocalAddress: " + inLocalAddress);
System.err.println("inRemoteAddress: " + inRemoteAddress);
System.err.println("outLocalAddress: " + outLocalAddress);
@@ -127,14 +130,42 @@
assertEquals(outLocalAddress.getAddress(), ss.getInetAddress());
assertEquals(outRemoteAddress.getAddress(), ss.getInetAddress());
+ assertFalse(ssc.socket().isClosed());
+ assertTrue(ssc.socket().isBound());
+ assertTrue(in.isConnected());
+ assertTrue(in.socket().isConnected());
+ assertTrue(out.socket().isConnected());
+ assertTrue(out.isConnected());
+
in.close();
out.close();
ssc.close();
+ assertTrue(ssc.socket().isClosed());
+ assertTrue(ssc.socket().isBound());
+ assertFalse(in.isConnected());
+ assertFalse(in.socket().isConnected());
+ assertFalse(out.socket().isConnected());
+ assertFalse(out.isConnected());
+
assertNull(in.socket().getRemoteSocketAddress());
assertNull(out.socket().getRemoteSocketAddress());
- assertEquals(in.socket().getLocalSocketAddress(), ss.getLocalSocketAddress());
+ // As per docs and RI - server socket local address methods continue to return the bind()
+ // addresses even after close().
+ assertEquals(listenAddress, ssc.socket().getLocalSocketAddress());
+
+ // As per docs and RI - socket local address methods return the wildcard address before
+ // bind() and after close(), but the port will be the same as it was before close().
+ InetSocketAddress inLocalAddressAfterClose =
+ (InetSocketAddress) in.socket().getLocalSocketAddress();
+ assertTrue(inLocalAddressAfterClose.getAddress().isAnyLocalAddress());
+ assertEquals(inLocalAddress.getPort(), inLocalAddressAfterClose.getPort());
+
+ InetSocketAddress outLocalAddressAfterClose =
+ (InetSocketAddress) out.socket().getLocalSocketAddress();
+ assertTrue(outLocalAddressAfterClose.getAddress().isAnyLocalAddress());
+ assertEquals(outLocalAddress.getPort(), outLocalAddressAfterClose.getPort());
}
// SocketOptions.setOption has weird behavior for setSoLinger/SO_LINGER.
@@ -286,6 +317,42 @@
serverSocket.close();
}
+ public void testInitialState() throws Exception {
+ Socket s = new Socket();
+ try {
+ assertFalse(s.isBound());
+ assertFalse(s.isClosed());
+ assertFalse(s.isConnected());
+ assertEquals(-1, s.getLocalPort());
+ assertTrue(s.getLocalAddress().isAnyLocalAddress());
+ assertNull(s.getLocalSocketAddress());
+ assertNull(s.getInetAddress());
+ assertEquals(0, s.getPort());
+ assertNull(s.getRemoteSocketAddress());
+ assertFalse(s.getReuseAddress());
+ assertNull(s.getChannel());
+ } finally {
+ s.close();
+ }
+ }
+
+ public void testStateAfterClose() throws Exception {
+ Socket s = new Socket();
+ s.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 0));
+ InetSocketAddress boundAddress = (InetSocketAddress) s.getLocalSocketAddress();
+ s.close();
+
+ assertTrue(s.isBound());
+ assertTrue(s.isClosed());
+ assertFalse(s.isConnected());
+ assertTrue(s.getLocalAddress().isAnyLocalAddress());
+ assertEquals(boundAddress.getPort(), s.getLocalPort());
+
+ InetSocketAddress localAddressAfterClose = (InetSocketAddress) s.getLocalSocketAddress();
+ assertTrue(localAddressAfterClose.getAddress().isAnyLocalAddress());
+ assertEquals(boundAddress.getPort(), localAddressAfterClose.getPort());
+ }
+
static class MockServer {
private ExecutorService executor;
private ServerSocket serverSocket;
diff --git a/luni/src/test/java/libcore/java/net/URITest.java b/luni/src/test/java/libcore/java/net/URITest.java
index 0267699..7f4c086 100644
--- a/luni/src/test/java/libcore/java/net/URITest.java
+++ b/luni/src/test/java/libcore/java/net/URITest.java
@@ -529,7 +529,7 @@
assertEquals("b/c", a.relativize(b).toString()); // RI assumes a directory
}
- public void testParseServerAuthorityInvalidAuthority() throws Exception {
+ public void testParseServerAuthorityInvalidPortMinus() throws Exception {
URI uri = new URI("http://host:-2/");
assertEquals("host:-2", uri.getAuthority());
assertNull(uri.getHost());
@@ -541,6 +541,30 @@
}
}
+ public void testParseServerAuthorityInvalidPortPlus() throws Exception {
+ URI uri = new URI("http://host:+2/");
+ assertEquals("host:+2", uri.getAuthority());
+ assertNull(uri.getHost());
+ assertEquals(-1, uri.getPort());
+ try {
+ uri.parseServerAuthority();
+ fail();
+ } catch (URISyntaxException expected) {
+ }
+ }
+
+ public void testParseServerAuthorityInvalidPortNonASCII() throws Exception {
+ URI uri = new URI("http://host:١٢٣/"); // 123 in arabic
+ assertEquals("host:١٢٣", uri.getAuthority());
+ assertNull(uri.getHost());
+ assertEquals(-1, uri.getPort());
+ try {
+ uri.parseServerAuthority();
+ fail();
+ } catch (URISyntaxException expected) {
+ }
+ }
+
public void testParseServerAuthorityOmittedAuthority() throws Exception {
URI uri = new URI("http:file");
uri.parseServerAuthority(); // does nothing!
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index fc93ee6..b40976e 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -74,9 +74,9 @@
import libcore.javax.net.ssl.TestSSLContext;
import tests.net.StuckServer;
-import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AFTER_READING_REQUEST;
import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
+import static com.google.mockwebserver.SocketPolicy.FAIL_HANDSHAKE;
import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
@@ -335,49 +335,6 @@
assertEquals(0, server.takeRequest().getSequenceNumber());
}
- public void testRetryableRequestBodyAfterBrokenConnection() throws Exception {
- // Use SSL to make an alternate route available.
- TestSSLContext testSSLContext = TestSSLContext.create();
- server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
-
- server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(
- DISCONNECT_AFTER_READING_REQUEST));
- server.enqueue(new MockResponse().setBody("abc"));
- server.play();
-
- HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("").openConnection();
- connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
- connection.setDoOutput(true);
- OutputStream out = connection.getOutputStream();
- out.write(new byte[] {1, 2, 3});
- out.close();
- assertContent("abc", connection);
-
- assertEquals(0, server.takeRequest().getSequenceNumber());
- assertEquals(0, server.takeRequest().getSequenceNumber());
- }
-
- public void testNonRetryableRequestBodyAfterBrokenConnection() throws Exception {
- TestSSLContext testSSLContext = TestSSLContext.create();
- server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
- server.enqueue(new MockResponse().setBody("abc")
- .setSocketPolicy(DISCONNECT_AFTER_READING_REQUEST));
- server.play();
-
- HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/a").openConnection();
- connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
- connection.setDoOutput(true);
- connection.setFixedLengthStreamingMode(3);
- OutputStream out = connection.getOutputStream();
- out.write(new byte[] {1, 2, 3});
- out.close();
- try {
- connection.getInputStream();
- fail();
- } catch (IOException expected) {
- }
- }
-
enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS }
public void test_chunkedUpload_byteByByte() throws Exception {
@@ -518,28 +475,6 @@
}
}
- public void testConnectViaHttpsWithSSLFallback() throws IOException, InterruptedException {
- TestSSLContext testSSLContext = TestSSLContext.create();
-
- server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
- server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START));
- server.enqueue(new MockResponse().setBody("this response comes via SSL"));
- server.play();
-
- HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection();
- connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
-
- assertContent("this response comes via SSL", connection);
-
- // The first request will be an incomplete (bookkeeping) request
- // that the server disconnected from at start.
- server.takeRequest();
-
- // The request will be retried.
- RecordedRequest request = server.takeRequest();
- assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
- }
-
/**
* Verify that we don't retry connections on certificate verification errors.
*
@@ -1126,25 +1061,37 @@
}
/**
- * Obnoxiously test that the chunk sizes transmitted exactly equal the
- * requested data+chunk header size. Although setChunkedStreamingMode()
- * isn't specific about whether the size applies to the data or the
- * complete chunk, the RI interprets it as a complete chunk.
+ * Test that request body chunking works. This test has been relaxed from treating
+ * the {@link java.net.HttpURLConnection#setChunkedStreamingMode(int)}
+ * chunk length as being fixed because OkHttp no longer guarantees
+ * the fixed chunk size. Instead, we check that chunking takes place
+ * and we force the chunk size with flushes.
*/
public void testSetChunkedStreamingMode() throws IOException, InterruptedException {
server.enqueue(new MockResponse());
server.play();
HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
- urlConnection.setChunkedStreamingMode(8);
+ // Later releases of Android ignore the value for chunkLength if it is > 0 and default to
+ // a fixed chunkLength. During the change-over period while the chunkLength indicates the
+ // chunk buffer size (inc. header) the chunkLength has to be >= 8. This enables the flush()
+ // to dictate the size of the chunks.
+ urlConnection.setChunkedStreamingMode(50 /* chunkLength */);
urlConnection.setDoOutput(true);
OutputStream outputStream = urlConnection.getOutputStream();
- outputStream.write("ABCDEFGHIJKLMNOPQ".getBytes("US-ASCII"));
+ String outputString = "ABCDEFGH";
+ byte[] outputBytes = outputString.getBytes("US-ASCII");
+ int targetChunkSize = 3;
+ for (int i = 0; i < outputBytes.length; i += targetChunkSize) {
+ int count = i + targetChunkSize < outputBytes.length ? 3 : outputBytes.length - i;
+ outputStream.write(outputBytes, i, count);
+ outputStream.flush();
+ }
assertEquals(200, urlConnection.getResponseCode());
RecordedRequest request = server.takeRequest();
- assertEquals("ABCDEFGHIJKLMNOPQ", new String(request.getBody(), "US-ASCII"));
- assertEquals(Arrays.asList(3, 3, 3, 3, 3, 2), request.getChunkSizes());
+ assertEquals(outputString, new String(request.getBody(), "US-ASCII"));
+ assertEquals(Arrays.asList(3, 3, 2), request.getChunkSizes());
}
public void testAuthenticateWithFixedLengthStreaming() throws Exception {
@@ -2251,22 +2198,21 @@
}
}
+ // http://code.google.com/p/android/issues/detail?id=16895
public void testUrlWithSpaceInHostViaHttpProxy() throws Exception {
server.enqueue(new MockResponse());
server.play();
URLConnection urlConnection = new URL("http://and roid.com/")
.openConnection(server.toProxyAddress());
- try {
- urlConnection.getInputStream();
- fail(); // the RI makes a bogus proxy request for "GET http://and roid.com/ HTTP/1.1"
- } catch (UnknownHostException expected) {
- }
+
+ // This test is to check that a NullPointerException is not thrown.
+ urlConnection.getInputStream();
}
public void testSslFallback() throws Exception {
TestSSLContext testSSLContext = TestSSLContext.create();
server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
- server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
server.enqueue(new MockResponse().setBody("This required a 2nd handshake"));
server.play();
diff --git a/luni/src/test/java/libcore/java/net/URLTest.java b/luni/src/test/java/libcore/java/net/URLTest.java
index a18816b..07e651c 100644
--- a/luni/src/test/java/libcore/java/net/URLTest.java
+++ b/luni/src/test/java/libcore/java/net/URLTest.java
@@ -350,7 +350,7 @@
}
}
- public void testNegativePort() throws Exception {
+ public void testPortWithMinusSign() throws Exception {
try {
new URL("http://host:-2/");
fail();
@@ -358,6 +358,22 @@
}
}
+ public void testPortWithPlusSign() throws Exception {
+ try {
+ new URL("http://host:+2/");
+ fail();
+ } catch (MalformedURLException expected) {
+ }
+ }
+
+ public void testPortNonASCII() throws Exception {
+ try {
+ new URL("http://host:١٢٣/"); // 123 in arabic
+ fail();
+ } catch (MalformedURLException expected) {
+ }
+ }
+
public void testNegativePortEqualsPlaceholder() throws Exception {
try {
new URL("http://host:-1/");
diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java
new file mode 100644
index 0000000..a7eac3a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java
@@ -0,0 +1,1073 @@
+package libcore.java.nio.channels;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.StandardSocketOptions;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.MembershipKey;
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+/**
+ * Tests associated with multicast behavior of DatagramChannel.
+ */
+public class DatagramChannelMulticastTest extends TestCase {
+
+ private static InetAddress lookup(String s) {
+ try {
+ return InetAddress.getByName(s);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ // These IP addresses aren't inherently "good" or "bad"; they're just used like that.
+ // We use the "good" addresses for our actual group, and the "bad" addresses are for
+ // a group that we won't actually set up.
+ private static final InetAddress GOOD_MULTICAST_IPv4 = lookup("239.255.0.1");
+ private static final InetAddress BAD_MULTICAST_IPv4 = lookup("239.255.0.2");
+ private static final InetAddress GOOD_MULTICAST_IPv6 = lookup("ff05::7:7");
+ private static final InetAddress BAD_MULTICAST_IPv6 = lookup("ff05::7:8");
+
+ // Special addresses.
+ private static final InetAddress WILDCARD_IPv4 = lookup("0.0.0.0");
+ private static final InetAddress WILDCARD_IPv6 = lookup("::");
+
+ // Arbitrary unicast addresses. Used when the value doesn't actually matter. e.g. for source
+ // filters.
+ private static final InetAddress UNICAST_IPv4_1 = lookup("192.168.1.1");
+ private static final InetAddress UNICAST_IPv4_2 = lookup("192.168.1.2");
+ private static final InetAddress UNICAST_IPv6_1 = lookup("2001:db8::1");
+ private static final InetAddress UNICAST_IPv6_2 = lookup("2001:db8::2");
+
+ private NetworkInterface networkInterface1;
+ private NetworkInterface IPV6networkInterface1;
+ private NetworkInterface loopbackInterface;
+
+ @Override
+ protected void setUp() throws Exception {
+ // The loopback interface isn't actually useful for sending/receiving multicast messages
+ // but it can be used as a dummy for tests where that does not matter.
+ loopbackInterface = NetworkInterface.getByInetAddress(InetAddress.getLoopbackAddress());
+ assertNotNull(loopbackInterface);
+ assertTrue(loopbackInterface.isLoopback());
+ assertFalse(loopbackInterface.supportsMulticast());
+
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+
+ // only consider interfaces that have addresses associated with them.
+ // Otherwise tests don't work so well
+ if (interfaces != null) {
+ boolean atLeastOneInterface = false;
+ while (interfaces.hasMoreElements() && (atLeastOneInterface == false)) {
+ networkInterface1 = interfaces.nextElement();
+ if (willWorkForMulticast(networkInterface1)) {
+ atLeastOneInterface = true;
+ }
+ }
+
+ assertTrue("Test environment must have at least one network interface capable of multicast",
+ atLeastOneInterface);
+
+ // Find the first multicast-compatible interface that supports IPV6 if one exists
+ interfaces = NetworkInterface.getNetworkInterfaces();
+
+ boolean found = false;
+ while (interfaces.hasMoreElements() && !found) {
+ NetworkInterface nextInterface = interfaces.nextElement();
+ if (willWorkForMulticast(nextInterface)) {
+ Enumeration<InetAddress> addresses = nextInterface.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ final InetAddress nextAddress = addresses.nextElement();
+ if (nextAddress instanceof Inet6Address) {
+ IPV6networkInterface1 = nextInterface;
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void test_open() throws IOException {
+ DatagramChannel dc = DatagramChannel.open();
+
+ // Unlike MulticastSocket, DatagramChannel has SO_REUSEADDR set to false by default.
+ assertFalse(dc.getOption(StandardSocketOptions.SO_REUSEADDR));
+
+ assertNull(dc.getLocalAddress());
+ assertTrue(dc.isOpen());
+ assertFalse(dc.isConnected());
+ }
+
+ public void test_bind_null() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ assertNotNull(dc.getLocalAddress());
+ assertTrue(dc.isOpen());
+ assertFalse(dc.isConnected());
+
+ dc.close();
+ try {
+ dc.getLocalAddress();
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ assertFalse(dc.isOpen());
+ assertFalse(dc.isConnected());
+ }
+
+ public void test_joinAnySource_afterClose() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ dc.close();
+ try {
+ dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
+
+ public void test_joinAnySource_nullGroupAddress() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(null, networkInterface1);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinAnySource_nullNetworkInterface() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(GOOD_MULTICAST_IPv4, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinAnySource_nonMulticastGroupAddress_IPv4() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(UNICAST_IPv4_1, networkInterface1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinAnySource_nonMulticastGroupAddress_IPv6() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(UNICAST_IPv6_1, networkInterface1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinAnySource_IPv4() throws Exception {
+ test_joinAnySource(GOOD_MULTICAST_IPv4, BAD_MULTICAST_IPv4);
+ }
+
+ public void test_joinAnySource_IPv6() throws Exception {
+ test_joinAnySource(GOOD_MULTICAST_IPv6, BAD_MULTICAST_IPv6);
+ }
+
+ private void test_joinAnySource(InetAddress group, InetAddress group2) throws Exception {
+ // Set up a receiver join the group on networkInterface1
+ DatagramChannel receiverChannel = createReceiverChannel();
+ InetSocketAddress localAddress = (InetSocketAddress) receiverChannel.getLocalAddress();
+ receiverChannel.join(group, networkInterface1);
+
+ String msg = "Hello World";
+ sendMulticastMessage(group, localAddress.getPort(), msg);
+
+ // now verify that we received the data as expected
+ ByteBuffer recvBuffer = ByteBuffer.allocate(100);
+ SocketAddress sourceAddress = receiverChannel.receive(recvBuffer);
+ assertNotNull(sourceAddress);
+ assertEquals(msg, new String(recvBuffer.array(), 0, recvBuffer.position()));
+
+ // now verify that we didn't receive the second message
+ String msg2 = "Hello World - Different Group";
+ sendMulticastMessage(group2, localAddress.getPort(), msg2);
+ recvBuffer.position(0);
+ SocketAddress sourceAddress2 = receiverChannel.receive(recvBuffer);
+ assertNull(sourceAddress2);
+
+ receiverChannel.close();
+ }
+
+ public void test_joinAnySource_processLimit() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ for (byte i = 1; i <= 25; i++) {
+ InetAddress groupAddress = Inet4Address.getByName("239.255.0." + i);
+ try {
+ dc.join(groupAddress, networkInterface1);
+ } catch (SocketException e) {
+ // There is a limit, that's ok according to the RI docs. For this test a lower bound of 20
+ // is used, which appears to be the default linux limit.
+ // See /proc/sys/net/ipv4/igmp_max_memberships
+ assertTrue(i > 20);
+ break;
+ }
+ }
+
+ dc.close();
+ }
+
+ public void test_joinAnySource_blockLimit() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey key = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ for (byte i = 1; i <= 15; i++) {
+ InetAddress sourceAddress = Inet4Address.getByName("10.0.0." + i);
+ try {
+ key.block(sourceAddress);
+ } catch (SocketException e) {
+ // There is a limit, that's ok according to the RI docs. For this test a lower bound of 10
+ // is used, which appears to be the default linux limit.
+ // See /proc/sys/net/ipv4/igmp_max_msf
+ assertTrue(i > 10);
+ break;
+ }
+ }
+
+ dc.close();
+ }
+
+ /** Confirms that calling join() does not cause an implicit bind() to take place. */
+ public void test_joinAnySource_doesNotCauseBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ assertNull(dc.getLocalAddress());
+
+ dc.close();
+ }
+
+ public void test_joinAnySource_networkInterfaces() throws Exception {
+ // Check that we can join on specific interfaces and that we only receive if data is
+ // received on that interface. This test is only really useful on devices with multiple
+ // non-loopback interfaces.
+
+ ArrayList<NetworkInterface> realInterfaces = new ArrayList<NetworkInterface>();
+ Enumeration<NetworkInterface> theInterfaces = NetworkInterface.getNetworkInterfaces();
+ while (theInterfaces.hasMoreElements()) {
+ NetworkInterface thisInterface = theInterfaces.nextElement();
+ if (thisInterface.getInetAddresses().hasMoreElements()) {
+ realInterfaces.add(thisInterface);
+ }
+ }
+
+ for (int i = 0; i < realInterfaces.size(); i++) {
+ NetworkInterface thisInterface = realInterfaces.get(i);
+ if (!thisInterface.supportsMulticast()) {
+ // Skip interfaces that do not support multicast - there's no point in proving
+ // they cannot send / receive multicast messages.
+ continue;
+ }
+
+ // get the first address on the interface
+
+ // start server which is joined to the group and has
+ // only asked for packets on this interface
+ Enumeration<InetAddress> addresses = thisInterface.getInetAddresses();
+
+ NetworkInterface sendingInterface = null;
+ InetAddress group = null;
+ if (addresses.hasMoreElements()) {
+ InetAddress firstAddress = addresses.nextElement();
+ if (firstAddress instanceof Inet4Address) {
+ group = GOOD_MULTICAST_IPv4;
+ sendingInterface = networkInterface1;
+ } else {
+ // if this interface only seems to support IPV6 addresses
+ group = GOOD_MULTICAST_IPv6;
+ sendingInterface = IPV6networkInterface1;
+ }
+ }
+
+ DatagramChannel dc = createReceiverChannel();
+ InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress();
+ dc.join(group, thisInterface);
+
+ // Now send out a package on sendingInterface. We should only see the packet if we send
+ // it on the same interface we are listening on (thisInterface).
+ String msg = "Hello World - Again" + thisInterface.getName();
+ sendMulticastMessage(group, localAddress.getPort(), msg, sendingInterface);
+
+ ByteBuffer recvBuffer = ByteBuffer.allocate(100);
+ SocketAddress sourceAddress = dc.receive(recvBuffer);
+ if (thisInterface.equals(sendingInterface)) {
+ assertEquals(msg, new String(recvBuffer.array(), 0, recvBuffer.position()));
+ } else {
+ assertNull(sourceAddress);
+ }
+
+ dc.close();
+ }
+ }
+
+ /** Confirms that the scope of each membership is network interface-level. */
+ public void test_join_canMixTypesOnDifferentInterfaces() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ MembershipKey membershipKey1 = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ MembershipKey membershipKey2 = dc.join(GOOD_MULTICAST_IPv4, loopbackInterface, UNICAST_IPv4_1);
+ assertNotSame(membershipKey1, membershipKey2);
+
+ dc.close();
+ }
+
+
+ private DatagramChannel createReceiverChannel() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(null /* leave the OS to determine the port, and use the wildcard address */);
+ configureChannelForReceiving(dc);
+ return dc;
+ }
+
+ public void test_joinAnySource_multiple_joins_IPv4()
+ throws Exception {
+ test_joinAnySource_multiple_joins(GOOD_MULTICAST_IPv4);
+ }
+
+ public void test_joinAnySource_multiple_joins_IPv6()
+ throws Exception {
+ test_joinAnySource_multiple_joins(GOOD_MULTICAST_IPv6);
+ }
+
+ private void test_joinAnySource_multiple_joins(InetAddress group) throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+
+ MembershipKey membershipKey1 = dc.join(group, networkInterface1);
+
+ MembershipKey membershipKey2 = dc.join(group, loopbackInterface);
+ assertFalse(membershipKey1.equals(membershipKey2));
+
+ MembershipKey membershipKey1_2 = dc.join(group, networkInterface1);
+ assertEquals(membershipKey1, membershipKey1_2);
+
+ dc.close();
+ }
+
+ public void test_joinAnySource_multicastLoopOption_IPv4() throws Exception {
+ test_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv4);
+ }
+
+ public void test_multicastLoopOption_IPv6() throws Exception {
+ test_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv6);
+ }
+
+ private void test_joinAnySource_multicastLoopOption(InetAddress group) throws Exception {
+ final String message = "Hello, world!";
+
+ DatagramChannel dc = createReceiverChannel();
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true /* enable loop */);
+ configureChannelForReceiving(dc);
+ dc.join(group, networkInterface1);
+
+ InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress();
+
+ // send the datagram
+ byte[] sendData = message.getBytes();
+ ByteBuffer sendBuffer = ByteBuffer.wrap(sendData);
+ dc.send(sendBuffer, new InetSocketAddress(group, localAddress.getPort()));
+
+ // receive the datagram
+ ByteBuffer recvBuffer = ByteBuffer.allocate(100);
+ SocketAddress sourceAddress = dc.receive(recvBuffer);
+ assertNotNull(sourceAddress);
+
+ String recvMessage = new String(recvBuffer.array(), 0, recvBuffer.position());
+ assertEquals(message, recvMessage);
+
+ // Turn off loop
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false /* enable loopback */);
+
+ // send another datagram
+ recvBuffer.position(0);
+ ByteBuffer sendBuffer2 = ByteBuffer.wrap(sendData);
+ dc.send(sendBuffer2, new InetSocketAddress(group, localAddress.getPort()));
+
+ SocketAddress sourceAddress2 = dc.receive(recvBuffer);
+ assertNull(sourceAddress2);
+
+ dc.close();
+ }
+
+ public void testMembershipKeyAccessors_IPv4() throws Exception {
+ testMembershipKeyAccessors(GOOD_MULTICAST_IPv4);
+ }
+
+ public void testMembershipKeyAccessors_IPv6() throws Exception {
+ testMembershipKeyAccessors(GOOD_MULTICAST_IPv6);
+ }
+
+ private void testMembershipKeyAccessors(InetAddress group) throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+
+ MembershipKey key = dc.join(group, networkInterface1);
+ assertSame(dc, key.channel());
+ assertSame(group, key.group());
+ assertTrue(key.isValid());
+ assertSame(networkInterface1, key.networkInterface());
+ assertNull(key.sourceAddress());
+ }
+
+ public void test_dropAnySource_twice_IPv4() throws Exception {
+ test_dropAnySource_twice(GOOD_MULTICAST_IPv4);
+ }
+
+ public void test_dropAnySource_twice_IPv6() throws Exception {
+ test_dropAnySource_twice(GOOD_MULTICAST_IPv6);
+ }
+
+ private void test_dropAnySource_twice(InetAddress group) throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(group, networkInterface1);
+
+ assertTrue(membershipKey.isValid());
+ membershipKey.drop();
+ assertFalse(membershipKey.isValid());
+
+ // Try to leave a group we are no longer a member of - should do nothing.
+ membershipKey.drop();
+
+ dc.close();
+ }
+
+ public void test_close_invalidatesMembershipKey() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+
+ assertTrue(membershipKey.isValid());
+
+ dc.close();
+
+ assertFalse(membershipKey.isValid());
+ }
+
+ public void test_block_null() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ try {
+ membershipKey.block(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ dc.close();
+ }
+
+ public void test_block_mixedAddressTypes_IPv4() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ try {
+ membershipKey.block(UNICAST_IPv6_1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ dc.close();
+ }
+
+ public void test_block_mixedAddressTypes_IPv6() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv6, networkInterface1);
+ try {
+ membershipKey.block(UNICAST_IPv4_1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ dc.close();
+ }
+
+ public void test_block_cannotBlockWithSourceSpecificMembership() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1, UNICAST_IPv4_1);
+ try {
+ membershipKey.block(UNICAST_IPv4_2);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
+ dc.close();
+ }
+
+ public void test_block_multipleBlocksIgnored() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ membershipKey.block(UNICAST_IPv4_1);
+
+ MembershipKey membershipKey2 = membershipKey.block(UNICAST_IPv4_1);
+ assertSame(membershipKey2, membershipKey);
+
+ dc.close();
+ }
+
+ public void test_block_wildcardAddress() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ try {
+ membershipKey.block(WILDCARD_IPv4);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ dc.close();
+ }
+
+ public void test_unblock_multipleUnblocksFail() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+
+ try {
+ membershipKey.unblock(UNICAST_IPv4_1);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
+ assertTrue(membershipKey.isValid());
+
+ membershipKey.block(UNICAST_IPv4_1);
+ membershipKey.unblock(UNICAST_IPv4_1);
+
+ try {
+ membershipKey.unblock(UNICAST_IPv4_1);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
+ dc.close();
+ }
+
+ public void test_unblock_null() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ membershipKey.block(UNICAST_IPv4_1);
+
+ try {
+ membershipKey.unblock(null);
+ fail();
+ } catch (IllegalStateException expected) {
+ // Either of these exceptions are fine
+ } catch (NullPointerException expected) {
+ // Either of these exception are fine
+ }
+
+ dc.close();
+ }
+
+ public void test_unblock_mixedAddressTypes_IPv4() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1);
+ try {
+ membershipKey.unblock(UNICAST_IPv6_1);
+ fail();
+ } catch (IllegalStateException expected) {
+ // Either of these exceptions are fine
+ } catch (IllegalArgumentException expected) {
+ // Either of these exceptions are fine
+ }
+
+ dc.close();
+ }
+
+ public void test_unblock_mixedAddressTypes_IPv6() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv6, networkInterface1);
+ try {
+ membershipKey.unblock(UNICAST_IPv4_1);
+ fail();
+ } catch (IllegalStateException expected) {
+ // Either of these exceptions are fine
+ } catch (IllegalArgumentException expected) {
+ // Either of these exceptions are fine
+ }
+
+ dc.close();
+ }
+
+ /** Checks that block() works when the receiver is bound to the multicast group address */
+ public void test_block_filtersAsExpected_groupBind_ipv4() throws Exception {
+ InetAddress ipv4LocalAddress = getLocalIpv4Address(networkInterface1);
+ test_block_filtersAsExpected(
+ ipv4LocalAddress /* senderBindAddress */,
+ GOOD_MULTICAST_IPv4 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv4 /* groupAddress */);
+ }
+
+ /** Checks that block() works when the receiver is bound to the multicast group address */
+ public void test_block_filtersAsExpected_groupBind_ipv6() throws Exception {
+ InetAddress ipv6LocalAddress = getLocalIpv6Address(IPV6networkInterface1);
+ test_block_filtersAsExpected(
+ ipv6LocalAddress /* senderBindAddress */,
+ GOOD_MULTICAST_IPv6 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv6 /* groupAddress */);
+ }
+
+ /** Checks that block() works when the receiver is bound to the "any" address */
+ public void test_block_filtersAsExpected_anyBind_ipv4() throws Exception {
+ InetAddress ipv4LocalAddress = getLocalIpv4Address(networkInterface1);
+ test_block_filtersAsExpected(
+ ipv4LocalAddress /* senderBindAddress */,
+ WILDCARD_IPv4 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv4 /* groupAddress */);
+ }
+
+ /** Checks that block() works when the receiver is bound to the "any" address */
+ public void test_block_filtersAsExpected_anyBind_ipv6() throws Exception {
+ InetAddress ipv6LocalAddress = getLocalIpv6Address(IPV6networkInterface1);
+ test_block_filtersAsExpected(
+ ipv6LocalAddress /* senderBindAddress */,
+ WILDCARD_IPv6 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv6 /* groupAddress */);
+ }
+
+ private void test_block_filtersAsExpected(
+ InetAddress senderBindAddress, InetAddress receiverBindAddress, InetAddress groupAddress)
+ throws Exception {
+
+ DatagramChannel sendingChannel = DatagramChannel.open();
+ // In order to block a sender the sender's address must be known. The sendingChannel is
+ // explicitly bound to a known, non-loopback address.
+ sendingChannel.bind(new InetSocketAddress(senderBindAddress, 0));
+ InetSocketAddress sendingAddress = (InetSocketAddress) sendingChannel.getLocalAddress();
+
+ DatagramChannel receivingChannel = DatagramChannel.open();
+ configureChannelForReceiving(receivingChannel);
+ receivingChannel.bind(
+ new InetSocketAddress(receiverBindAddress, 0) /* local port left to the OS to determine */);
+ InetSocketAddress localReceivingAddress =
+ (InetSocketAddress) receivingChannel.getLocalAddress();
+ InetSocketAddress groupSocketAddress =
+ new InetSocketAddress(groupAddress, localReceivingAddress.getPort());
+ MembershipKey membershipKey =
+ receivingChannel.join(groupSocketAddress.getAddress(), networkInterface1);
+
+ ByteBuffer receiveBuffer = ByteBuffer.allocate(10);
+
+ // Send a message. It should be received.
+ String msg1 = "Hello1";
+ sendMessage(sendingChannel, msg1, groupSocketAddress);
+ InetSocketAddress sourceAddress1 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
+ assertEquals(sourceAddress1, sendingAddress);
+ assertEquals(msg1, new String(receiveBuffer.array(), 0, receiveBuffer.position()));
+
+ // Now block the sender
+ membershipKey.block(sendingAddress.getAddress());
+
+ // Send a message. It should be filtered.
+ String msg2 = "Hello2";
+ sendMessage(sendingChannel, msg2, groupSocketAddress);
+ receiveBuffer.position(0);
+ InetSocketAddress sourceAddress2 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
+ assertNull(sourceAddress2);
+
+ // Now unblock the sender
+ membershipKey.unblock(sendingAddress.getAddress());
+
+ // Send a message. It should be received.
+ String msg3 = "Hello3";
+ sendMessage(sendingChannel, msg3, groupSocketAddress);
+ receiveBuffer.position(0);
+ InetSocketAddress sourceAddress3 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
+ assertEquals(sourceAddress3, sendingAddress);
+ assertEquals(msg3, new String(receiveBuffer.array(), 0, receiveBuffer.position()));
+
+ sendingChannel.close();
+ receivingChannel.close();
+ }
+
+ public void test_joinSourceSpecific_nullGroupAddress() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(null, networkInterface1, UNICAST_IPv4_1);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_afterClose() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ dc.close();
+ try {
+ dc.join(GOOD_MULTICAST_IPv4, networkInterface1, UNICAST_IPv4_1);
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
+
+ public void test_joinSourceSpecific_nullNetworkInterface() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(GOOD_MULTICAST_IPv4, null, UNICAST_IPv4_1);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_nonMulticastGroupAddress_IPv4() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(UNICAST_IPv4_1, networkInterface1, UNICAST_IPv4_1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_nonMulticastGroupAddress_IPv6() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(UNICAST_IPv6_1, IPV6networkInterface1, UNICAST_IPv6_1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_nullSourceAddress_IPv4() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(GOOD_MULTICAST_IPv4, networkInterface1, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_nullSourceAddress_IPv6() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(GOOD_MULTICAST_IPv6, networkInterface1, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_mixedAddressTypes() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(GOOD_MULTICAST_IPv4, networkInterface1, UNICAST_IPv6_1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ dc.join(GOOD_MULTICAST_IPv6, networkInterface1, UNICAST_IPv4_1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_nonUnicastSourceAddress_IPv4() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(GOOD_MULTICAST_IPv4, networkInterface1, BAD_MULTICAST_IPv4);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_nonUniicastSourceAddress_IPv6() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ try {
+ dc.join(GOOD_MULTICAST_IPv6, networkInterface1, BAD_MULTICAST_IPv6);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ dc.close();
+ }
+
+ public void test_joinSourceSpecific_multipleSourceAddressLimit() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ for (byte i = 1; i <= 20; i++) {
+ InetAddress sourceAddress = Inet4Address.getByAddress(new byte[] { 10, 0, 0, i});
+ try {
+ dc.join(GOOD_MULTICAST_IPv4, networkInterface1, sourceAddress);
+ } catch (SocketException e) {
+ // There is a limit, that's ok according to the RI docs. For this test a lower bound of 10
+ // is used, which appears to be the default linux limit. See /proc/sys/net/ipv4/igmp_max_msf
+ assertTrue(i > 10);
+ break;
+ }
+ }
+
+ dc.close();
+ }
+
+ /**
+ * Checks that a source-specific join() works when the receiver is bound to the multicast group
+ * address
+ */
+ public void test_joinSourceSpecific_null() throws Exception {
+ InetAddress ipv4LocalAddress = getLocalIpv4Address(networkInterface1);
+ test_joinSourceSpecific(
+ ipv4LocalAddress /* senderBindAddress */,
+ GOOD_MULTICAST_IPv4 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv4 /* groupAddress */,
+ UNICAST_IPv4_1 /* badSenderAddress */);
+ }
+
+ /**
+ * Checks that a source-specific join() works when the receiver is bound to the multicast group
+ * address
+ */
+ public void test_joinSourceSpecific_groupBind_ipv4() throws Exception {
+ InetAddress ipv4LocalAddress = getLocalIpv4Address(networkInterface1);
+ test_joinSourceSpecific(
+ ipv4LocalAddress /* senderBindAddress */,
+ GOOD_MULTICAST_IPv4 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv4 /* groupAddress */,
+ UNICAST_IPv4_1 /* badSenderAddress */);
+ }
+
+ /**
+ * Checks that a source-specific join() works when the receiver is bound to the multicast group
+ * address
+ */
+ public void test_joinSourceSpecific_groupBind_ipv6() throws Exception {
+ InetAddress ipv6LocalAddress = getLocalIpv6Address(IPV6networkInterface1);
+ test_joinSourceSpecific(
+ ipv6LocalAddress /* senderBindAddress */,
+ GOOD_MULTICAST_IPv6 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv6 /* groupAddress */,
+ UNICAST_IPv6_1 /* badSenderAddress */);
+ }
+
+ /** Checks that a source-specific join() works when the receiver is bound to the "any" address */
+ public void test_joinSourceSpecific_anyBind_ipv4() throws Exception {
+ InetAddress ipv4LocalAddress = getLocalIpv4Address(networkInterface1);
+ test_joinSourceSpecific(
+ ipv4LocalAddress /* senderBindAddress */,
+ WILDCARD_IPv4 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv4 /* groupAddress */,
+ UNICAST_IPv4_1 /* badSenderAddress */);
+ }
+
+ /** Checks that a source-specific join() works when the receiver is bound to the "any" address */
+ public void test_joinSourceSpecific_anyBind_ipv6() throws Exception {
+ InetAddress ipv6LocalAddress = getLocalIpv6Address(IPV6networkInterface1);
+ test_joinSourceSpecific(
+ ipv6LocalAddress /* senderBindAddress */,
+ WILDCARD_IPv6 /* receiverBindAddress */,
+ GOOD_MULTICAST_IPv6 /* groupAddress */,
+ UNICAST_IPv6_1 /* badSenderAddress */);
+ }
+
+ /**
+ * Checks that the source-specific membership is correctly source-filtering.
+ *
+ * @param senderBindAddress the address to bind the sender socket to
+ * @param receiverBindAddress the address to bind the receiver socket to
+ * @param groupAddress the group address to join
+ * @param badSenderAddress a unicast address to join to perform a negative test
+ */
+ private void test_joinSourceSpecific(
+ InetAddress senderBindAddress, InetAddress receiverBindAddress, InetAddress groupAddress,
+ InetAddress badSenderAddress)
+ throws Exception {
+ DatagramChannel sendingChannel = DatagramChannel.open();
+ // In order to be source-specific the sender's address must be known. The sendingChannel is
+ // explicitly bound to a known, non-loopback address.
+ sendingChannel.bind(new InetSocketAddress(senderBindAddress, 0));
+ InetSocketAddress sendingAddress = (InetSocketAddress) sendingChannel.getLocalAddress();
+
+ DatagramChannel receivingChannel = DatagramChannel.open();
+ receivingChannel.bind(
+ new InetSocketAddress(receiverBindAddress, 0) /* local port left to the OS to determine */);
+ configureChannelForReceiving(receivingChannel);
+
+ InetSocketAddress localReceivingAddress =
+ (InetSocketAddress) receivingChannel.getLocalAddress();
+ InetSocketAddress groupSocketAddress =
+ new InetSocketAddress(groupAddress, localReceivingAddress.getPort());
+ MembershipKey membershipKey1 = receivingChannel
+ .join(groupSocketAddress.getAddress(), networkInterface1, senderBindAddress);
+
+ ByteBuffer receiveBuffer = ByteBuffer.allocate(10);
+
+ // Send a message. It should be received.
+ String msg1 = "Hello1";
+ sendMessage(sendingChannel, msg1, groupSocketAddress);
+ InetSocketAddress sourceAddress1 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
+ assertEquals(sourceAddress1, sendingAddress);
+ assertEquals(msg1, new String(receiveBuffer.array(), 0, receiveBuffer.position()));
+
+ membershipKey1.drop();
+
+ receivingChannel.join(groupSocketAddress.getAddress(), networkInterface1, badSenderAddress);
+
+ // Send a message. It should not be received.
+ String msg2 = "Hello2";
+ sendMessage(sendingChannel, msg2, groupSocketAddress);
+ InetSocketAddress sourceAddress2 = (InetSocketAddress) receivingChannel.receive(receiveBuffer);
+ assertNull(sourceAddress2);
+
+ receivingChannel.close();
+ sendingChannel.close();
+ }
+
+ public void test_dropSourceSpecific_twice_IPv4() throws Exception {
+ test_dropSourceSpecific_twice(
+ GOOD_MULTICAST_IPv4 /* groupAddress */, UNICAST_IPv4_1 /* sourceAddress */);
+ }
+
+ public void test_dropSourceSpecific_twice_IPv6() throws Exception {
+ test_dropSourceSpecific_twice(
+ GOOD_MULTICAST_IPv6 /* groupAddress */, UNICAST_IPv6_1 /* sourceAddress */);
+ }
+
+ private void test_dropSourceSpecific_twice(InetAddress groupAddress, InetAddress sourceAddress)
+ throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(groupAddress, networkInterface1, sourceAddress);
+
+ assertTrue(membershipKey.isValid());
+ membershipKey.drop();
+ assertFalse(membershipKey.isValid());
+
+ // Try to leave a group we are no longer a member of - should do nothing.
+ membershipKey.drop();
+
+ dc.close();
+ }
+
+ public void test_dropSourceSpecific_sourceKeysAreIndependent_IPv4() throws Exception {
+ test_dropSourceSpecific_sourceKeysAreIndependent(
+ GOOD_MULTICAST_IPv4 /* groupAddress */,
+ UNICAST_IPv4_1 /* sourceAddress1 */,
+ UNICAST_IPv4_2 /* sourceAddress2 */);
+ }
+
+ public void test_dropSourceSpecific_sourceKeysAreIndependent_IPv6() throws Exception {
+ test_dropSourceSpecific_sourceKeysAreIndependent(
+ GOOD_MULTICAST_IPv6 /* groupAddress */,
+ UNICAST_IPv6_1 /* sourceAddress1 */,
+ UNICAST_IPv6_2 /* sourceAddress2 */);
+ }
+
+ private void test_dropSourceSpecific_sourceKeysAreIndependent(
+ InetAddress groupAddress, InetAddress sourceAddress1, InetAddress sourceAddress2)
+ throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey1 = dc.join(groupAddress, networkInterface1, sourceAddress1);
+ MembershipKey membershipKey2 = dc.join(groupAddress, networkInterface1, sourceAddress2);
+ assertFalse(membershipKey1.equals(membershipKey2));
+ assertTrue(membershipKey1.isValid());
+ assertTrue(membershipKey2.isValid());
+
+ membershipKey1.drop();
+
+ assertFalse(membershipKey1.isValid());
+ assertTrue(membershipKey2.isValid());
+
+ dc.close();
+ }
+
+ public void test_drop_keyBehaviorAfterDrop() throws Exception {
+ DatagramChannel dc = createReceiverChannel();
+ MembershipKey membershipKey = dc.join(GOOD_MULTICAST_IPv4, networkInterface1, UNICAST_IPv4_1);
+ membershipKey.drop();
+ assertFalse(membershipKey.isValid());
+
+ try {
+ membershipKey.block(UNICAST_IPv4_1);
+ } catch (IllegalStateException expected) {
+ }
+
+ try {
+ membershipKey.unblock(UNICAST_IPv4_1);
+ } catch (IllegalStateException expected) {
+ }
+
+ assertSame(dc, membershipKey.channel());
+ assertSame(GOOD_MULTICAST_IPv4, membershipKey.group());
+ assertSame(UNICAST_IPv4_1, membershipKey.sourceAddress());
+ assertSame(networkInterface1, membershipKey.networkInterface());
+ }
+
+ private static void configureChannelForReceiving(DatagramChannel receivingChannel)
+ throws Exception {
+
+ // NOTE: At the time of writing setSoTimeout() has no effect in the RI, making these tests hang
+ // if the channel is in blocking mode. configureBlocking(false) is used instead and rely on the
+ // network to the local host being instantaneous.
+ // receivingChannel.socket().setSoTimeout(200);
+ // receivingChannel.configureBlocking(true);
+ receivingChannel.configureBlocking(false);
+ }
+
+ private static boolean willWorkForMulticast(NetworkInterface iface) throws IOException {
+ return iface.isUp()
+ // Typically loopback interfaces do not support multicast, but they are ruled out
+ // explicitly here anyway.
+ && !iface.isLoopback() && iface.supportsMulticast()
+ && iface.getInetAddresses().hasMoreElements();
+ }
+
+ private static void sendMulticastMessage(InetAddress group, int port, String msg)
+ throws IOException {
+ sendMulticastMessage(group, port, msg, null /* networkInterface */);
+ }
+
+ private static void sendMulticastMessage(
+ InetAddress group, int port, String msg, NetworkInterface sendingInterface)
+ throws IOException {
+ // Any datagram socket can send to a group. It does not need to have joined the group.
+ DatagramChannel dc = DatagramChannel.open();
+ if (sendingInterface != null) {
+ // For some reason, if set, this must be set to a real (non-loopback) device for an IPv6
+ // group, but can be loopback for an IPv4 group.
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, sendingInterface);
+ }
+ sendMessage(dc, msg, new InetSocketAddress(group, port));
+ dc.close();
+ }
+
+ private static void sendMessage(
+ DatagramChannel sendingChannel, String msg, InetSocketAddress targetAddress)
+ throws IOException {
+
+ ByteBuffer sendBuffer = ByteBuffer.wrap(msg.getBytes());
+ sendingChannel.send(sendBuffer, targetAddress);
+ }
+
+ private static InetAddress getLocalIpv4Address(NetworkInterface networkInterface) {
+ for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
+ if (interfaceAddress.getAddress() instanceof Inet4Address) {
+ return interfaceAddress.getAddress();
+ }
+ }
+ throw new AssertionFailedError("Unable to find local IPv4 address for " + networkInterface);
+ }
+
+ private static InetAddress getLocalIpv6Address(NetworkInterface networkInterface) {
+ for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
+ if (interfaceAddress.getAddress() instanceof Inet6Address) {
+ return interfaceAddress.getAddress();
+ }
+ }
+ throw new AssertionFailedError("Unable to find local IPv6 address for " + networkInterface);
+ }
+
+}
diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
index 13757d2..cd4bb22 100644
--- a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
@@ -16,9 +16,22 @@
package libcore.java.nio.channels;
+import java.io.IOException;
import java.net.DatagramSocket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
+import java.nio.channels.UnresolvedAddressException;
+import java.util.Enumeration;
+import java.util.Set;
public class DatagramChannelTest extends junit.framework.TestCase {
public void test_read_intoReadOnlyByteArrays() throws Exception {
@@ -48,11 +61,509 @@
DatagramChannel dc = DatagramChannel.open();
try {
dc.configureBlocking(false);
- dc.socket().bind(null);
+ dc.bind(null);
// Should return immediately, since we're non-blocking.
assertNull(dc.receive(ByteBuffer.allocate(2048)));
} finally {
dc.close();
}
}
+
+ public void testInitialState() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ assertNull(dc.getLocalAddress());
+
+ DatagramSocket socket = dc.socket();
+ assertFalse(socket.isBound());
+ assertFalse(socket.getBroadcast());
+ assertFalse(socket.isClosed());
+ assertFalse(socket.isConnected());
+ assertEquals(0, socket.getLocalPort());
+ assertTrue(socket.getLocalAddress().isAnyLocalAddress());
+ assertNull(socket.getLocalSocketAddress());
+ assertNull(socket.getInetAddress());
+ assertEquals(-1, socket.getPort());
+ assertNull(socket.getRemoteSocketAddress());
+ assertFalse(socket.getReuseAddress());
+
+ assertSame(dc, socket.getChannel());
+ } finally {
+ dc.close();
+ }
+ }
+
+ public void test_supportedOptions() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ Set<SocketOption<?>> options = dc.supportedOptions();
+
+ // Probe some values. This is not intended to be complete.
+ assertTrue(options.contains(StandardSocketOptions.SO_REUSEADDR));
+ assertFalse(options.contains(StandardSocketOptions.TCP_NODELAY));
+ }
+
+ public void test_getOption_unsupportedOption() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ dc.getOption(StandardSocketOptions.TCP_NODELAY);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+
+ dc.close();
+ }
+
+ public void test_getOption_afterClose() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.close();
+
+ try {
+ dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ fail();
+ } catch (ClosedChannelException expected) {}
+ }
+
+ public void test_setOption_afterClose() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.close();
+
+ try {
+ dc.setOption(StandardSocketOptions.SO_RCVBUF, 1234);
+ fail();
+ } catch (ClosedChannelException expected) {}
+ }
+
+ public void test_getOption_SO_RCVBUF_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ int value = dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertTrue(value > 0);
+ assertEquals(value, dc.socket().getReceiveBufferSize());
+
+ dc.close();
+ }
+
+ public void test_setOption_SO_RCVBUF_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetReceiveBufferSizeOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetReceiveBufferSizeOption(DatagramChannel dc) throws IOException {
+ int initialValue = dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ try {
+ dc.setOption(StandardSocketOptions.SO_RCVBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ int actualValue = dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, dc.socket().getReceiveBufferSize());
+
+ int newBufferSize = initialValue - 1;
+ dc.setOption(StandardSocketOptions.SO_RCVBUF, newBufferSize);
+ actualValue = dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, dc.socket().getReceiveBufferSize());
+ }
+
+ public void test_getOption_SO_SNDBUF_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ int value = dc.getOption(StandardSocketOptions.SO_SNDBUF);
+ assertTrue(value > 0);
+ assertEquals(value, dc.socket().getSendBufferSize());
+
+ dc.close();
+ }
+
+ public void test_setOption_SO_SNDBUF_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetSendBufferSizeOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetSendBufferSizeOption(DatagramChannel dc) throws IOException {
+ int initialValue = dc.getOption(StandardSocketOptions.SO_SNDBUF);
+ try {
+ dc.setOption(StandardSocketOptions.SO_SNDBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ int actualValue = dc.getOption(StandardSocketOptions.SO_SNDBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, dc.socket().getSendBufferSize());
+
+ int newBufferSize = initialValue - 1;
+ dc.setOption(StandardSocketOptions.SO_SNDBUF, newBufferSize);
+ actualValue = dc.getOption(StandardSocketOptions.SO_SNDBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, dc.socket().getSendBufferSize());
+ }
+
+ public void test_getOption_IP_MULTICAST_IF_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ NetworkInterface networkInterface = dc.getOption(StandardSocketOptions.IP_MULTICAST_IF);
+ assertNull(networkInterface);
+
+ dc.close();
+ }
+
+ public void test_getOption_IP_MULTICAST_IF_nullCheck() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, null);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_IF_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+ assertTrue(networkInterfaces.hasMoreElements());
+ while (networkInterfaces.hasMoreElements()) {
+ trySetNetworkInterfaceOption(dc, networkInterfaces.nextElement());
+ }
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_IF_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(new InetSocketAddress(Inet4Address.getLoopbackAddress(), 0));
+
+ Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+ assertTrue(networkInterfaces.hasMoreElements());
+ while (networkInterfaces.hasMoreElements()) {
+ trySetNetworkInterfaceOption(dc, networkInterfaces.nextElement());
+ }
+
+ dc.close();
+ }
+
+ private static void trySetNetworkInterfaceOption(
+ DatagramChannel dc, NetworkInterface networkInterface) throws IOException {
+
+ NetworkInterface initialValue = dc.getOption(StandardSocketOptions.IP_MULTICAST_IF);
+ try {
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, null);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ assertEquals(initialValue, dc.getOption(StandardSocketOptions.IP_MULTICAST_IF));
+
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
+ NetworkInterface actualValue =
+ dc.getOption(StandardSocketOptions.IP_MULTICAST_IF);
+ assertEquals(networkInterface, actualValue);
+ }
+
+ public void test_getOption_IP_MULTICAST_LOOP_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ assertTrue(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.close();
+ }
+
+ public void test_getOption_IP_MULTICAST_LOOP_nullCheck() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, null);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_LOOP_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ assertTrue(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false);
+ assertFalse(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_LOOP_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(new InetSocketAddress(Inet4Address.getLoopbackAddress(), 0));
+
+ assertTrue(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false);
+ assertFalse(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.close();
+ }
+
+ public void test_getOption_IP_MULTICAST_TTL_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ int value = dc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ assertEquals(1, value);
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_TTL_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetMulticastTtlOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetMulticastTtlOption(DatagramChannel dc) throws IOException {
+ int initialValue = dc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ try {
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_TTL, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ int actualValue = dc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ assertEquals(initialValue, actualValue);
+
+ int newTtl = initialValue + 1;
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_TTL, newTtl);
+ actualValue = dc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ assertEquals(newTtl, actualValue);
+ }
+
+ public void test_setOption_IP_MULTICAST_TTL_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(null);
+
+ trySetMulticastTtlOption(dc);
+
+ dc.close();
+ }
+
+ public void test_getOption_SO_BROADCAST_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ assertFalse(dc.getOption(StandardSocketOptions.SO_BROADCAST));
+
+ dc.close();
+ }
+
+ public void test_setOption_SO_BROADCAST_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetSoBroadcastOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetSoBroadcastOption(DatagramChannel dc) throws IOException {
+ boolean initialValue = dc.getOption(StandardSocketOptions.SO_BROADCAST);
+
+ dc.setOption(StandardSocketOptions.SO_BROADCAST, !initialValue);
+ boolean actualValue = dc.getOption(StandardSocketOptions.SO_BROADCAST);
+ assertEquals(!initialValue, actualValue);
+ }
+
+ public void test_setOption_SO_BROADCAST_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(null);
+
+ trySetSoBroadcastOption(dc);
+
+ dc.close();
+ }
+
+ public void test_getOption_IP_TOS_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ int value = dc.getOption(StandardSocketOptions.IP_TOS);
+ assertEquals(0, value);
+ assertEquals(value, dc.socket().getTrafficClass());
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_TOS_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetTosOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetTosOption(DatagramChannel dc) throws IOException {
+ int initialValue = dc.getOption(StandardSocketOptions.IP_TOS);
+ try {
+ dc.setOption(StandardSocketOptions.IP_TOS, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ assertEquals(initialValue, (int) dc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(initialValue, dc.socket().getTrafficClass());
+
+ try {
+ dc.setOption(StandardSocketOptions.IP_TOS, 256);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ assertEquals(initialValue, (int) dc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(initialValue, dc.socket().getTrafficClass());
+
+ int newValue = (initialValue + 1) % 255;
+ dc.setOption(StandardSocketOptions.IP_TOS, newValue);
+ assertEquals(newValue, (int) dc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(newValue, dc.socket().getTrafficClass());
+ }
+
+ public void test_setOption_IP_TOS_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(null);
+
+ trySetTosOption(dc);
+
+ dc.close();
+ }
+
+ public void test_getOption_SO_REUSEADDR_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ boolean value = dc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ assertFalse(value);
+ assertFalse(dc.socket().getReuseAddress());
+
+ dc.close();
+ }
+
+ public void test_setOption_SO_REUSEADDR_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ boolean initialValue = dc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ dc.setOption(StandardSocketOptions.SO_REUSEADDR, !initialValue);
+ assertEquals(!initialValue, (boolean) dc.getOption(StandardSocketOptions.SO_REUSEADDR));
+ assertEquals(!initialValue, dc.socket().getReuseAddress());
+
+ dc.close();
+ }
+
+ public void test_bind_unresolvedAddress() throws IOException {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ dc.bind(new InetSocketAddress("unresolvedname", 31415));
+ fail();
+ } catch (UnresolvedAddressException expected) {
+ }
+
+ assertNull(dc.getLocalAddress());
+ assertTrue(dc.isOpen());
+ assertFalse(dc.isConnected());
+
+ dc.close();
+ }
+
+ public void test_bind_noReuseAddress() throws Exception {
+ DatagramChannel dc1 = DatagramChannel.open();
+ dc1.setOption(StandardSocketOptions.SO_REUSEADDR, false);
+ DatagramChannel dc2 = DatagramChannel.open();
+ dc1.setOption(StandardSocketOptions.SO_REUSEADDR, false);
+
+ dc1.bind(null);
+
+ try {
+ dc2.bind(dc1.getLocalAddress());
+ fail();
+ } catch (IOException expected) {}
+
+ dc1.close();
+ dc2.close();
+ }
+
+ public void test_bind_withReuseAddress() throws Exception {
+ DatagramChannel dc1 = DatagramChannel.open();
+ dc1.setOption(StandardSocketOptions.SO_REUSEADDR, true);
+ DatagramChannel dc2 = DatagramChannel.open();
+ dc2.setOption(StandardSocketOptions.SO_REUSEADDR, true);
+
+ dc1.bind(null);
+ dc2.bind(dc1.getLocalAddress());
+
+ dc1.close();
+ dc2.close();
+ }
+
+ public void test_bind_any_IPv4() throws Exception {
+ test_bind_any(InetAddress.getByName("0.0.0.0"));
+ }
+
+ public void test_bind_any_IPv6() throws Exception {
+ test_bind_any(InetAddress.getByName("::"));
+ }
+
+ private void test_bind_any(InetAddress bindAddress) throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(new InetSocketAddress(bindAddress, 0));
+
+ assertTrue(dc.isOpen());
+ assertFalse(dc.isConnected());
+
+ InetSocketAddress actualAddress = (InetSocketAddress) dc.getLocalAddress();
+ assertTrue(actualAddress.getAddress().isAnyLocalAddress());
+ assertTrue(actualAddress.getPort() > 0);
+
+ dc.close();
+ }
+
+ public void test_bind_loopback_IPv4() throws Exception {
+ test_bind(InetAddress.getByName("127.0.0.1"));
+ }
+
+ public void test_bind_loopback_IPv6() throws Exception {
+ test_bind(InetAddress.getByName("::1"));
+ }
+
+ public void test_bind_IPv4() throws Exception {
+ InetAddress bindAddress = getNonLoopbackNetworkInterfaceAddress(true /* ipv4 */);
+ test_bind(bindAddress);
+ }
+
+ public void test_bind_IPv6() throws Exception {
+ InetAddress bindAddress = getNonLoopbackNetworkInterfaceAddress(false /* ipv4 */);
+ test_bind(bindAddress);
+ }
+
+ private void test_bind(InetAddress bindAddress) throws IOException {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(new InetSocketAddress(bindAddress, 0));
+
+ InetSocketAddress actualAddress = (InetSocketAddress) dc.getLocalAddress();
+ assertEquals(bindAddress, actualAddress.getAddress());
+ assertTrue(actualAddress.getPort() > 0);
+
+ dc.close();
+ }
+
+ private static InetAddress getNonLoopbackNetworkInterfaceAddress(boolean ipv4)
+ throws SocketException {
+
+ Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+ while (networkInterfaces.hasMoreElements()) {
+ NetworkInterface networkInterface = networkInterfaces.nextElement();
+ if (networkInterface.isLoopback() || !networkInterface.isUp()) {
+ continue;
+ }
+ Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
+ while (inetAddresses.hasMoreElements()) {
+ InetAddress inetAddress = inetAddresses.nextElement();
+ if ( (ipv4 && inetAddress instanceof Inet4Address)
+ || (!ipv4 && inetAddress instanceof Inet6Address)) {
+ return inetAddress;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java b/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java
new file mode 100644
index 0000000..c06df04
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2014 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.java.nio.channels;
+
+import junit.framework.TestCase;
+
+import android.system.ErrnoException;
+import android.system.OsConstants;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import libcore.io.Libcore;
+
+import static libcore.io.IoUtils.closeQuietly;
+
+/**
+ * A test for file interrupt behavior. Because forcing a real file to block on read or write is
+ * difficult this test uses Unix FIFO / Named Pipes. FIFOs appear to Java as files but the test
+ * has more control over the available data. Reader will block until the other end writes, and
+ * writers can also be made to block.
+ *
+ * <p>Using FIFOs has a few drawbacks:
+ * <ol>
+ * <li>FIFOs are not supported from Java or the command-line on Android, so this test includes
+ * native code to create the FIFO.
+ * <li>FIFOs will not open() until there is both a reader and a writer of the FIFO; each test must
+ * always attach both ends or experience a blocked test.
+ * <li>FIFOs are not supported on some file systems. e.g. VFAT, so the test has to be particular
+ * about the temporary directory it uses to hold the FIFO.
+ * <li>Writes to FIFOs are buffered by the OS which makes blocking behavior more difficult to
+ * induce. See {@link ChannelWriter} and {@link StreamWriter}.
+ * </ol>
+ */
+public class FileIOInterruptTest extends TestCase {
+
+ private static File VOGAR_DEVICE_TEMP_DIR = new File("/data/data/file_io_interrupt_test");
+
+ private File fifoFile;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // This test relies on a FIFO file. The file system must support FIFOs, so we check the path.
+ String tmpDirName = System.getProperty("java.io.tmpdir");
+ File tmpDir;
+ if (tmpDirName.startsWith("/sdcard")) {
+ // Vogar execution on device runs in /sdcard. Unfortunately the file system used does not
+ // support FIFOs so the test must use one that is more likely to work.
+ if (!VOGAR_DEVICE_TEMP_DIR.exists()) {
+ assertTrue(VOGAR_DEVICE_TEMP_DIR.mkdir());
+ }
+ VOGAR_DEVICE_TEMP_DIR.deleteOnExit();
+ tmpDir = VOGAR_DEVICE_TEMP_DIR;
+ } else {
+ tmpDir = new File(tmpDirName);
+ }
+ fifoFile = new File(tmpDir, "fifo_file.tmp");
+ if (fifoFile.exists()) {
+ fifoFile.delete();
+ }
+ fifoFile.deleteOnExit();
+
+ // Create the fifo. This will throw an exception if the file system does not support it.
+ Libcore.os.mkfifo(fifoFile.getAbsolutePath(), OsConstants.S_IRWXU);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ fifoFile.delete();
+ VOGAR_DEVICE_TEMP_DIR.delete();
+ }
+
+ public void testStreamRead_exceptionWhenAlreadyClosed() throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+
+ FileInputStream fis = new FileInputStream(fifoFile);
+ fis.close();
+
+ byte[] buffer = new byte[10];
+ try {
+ fis.read(buffer);
+ fail();
+ } catch (IOException expected) {
+ assertSame(IOException.class, expected.getClass());
+ }
+
+ fifoWriter.tidyUp();
+ }
+
+ // This test fails on the RI: close() does not wake up a blocking FileInputStream.read() call.
+ public void testStreamRead_exceptionOnCloseWhenBlocked() throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+
+ FileInputStream fis = new FileInputStream(fifoFile);
+ StreamReader streamReader = new StreamReader(fis);
+ Thread streamReaderThread = createAndStartThread("StreamReader", streamReader);
+
+ // Delay until we can be fairly sure the reader thread is blocking.
+ streamReader.waitForThreadToBlock();
+
+ // Now close the OutputStream to see what happens.
+ fis.close();
+
+ // Test for expected behavior in the reader thread.
+ waitToDie(streamReaderThread);
+ assertSame(InterruptedIOException.class, streamReader.ioe.getClass());
+ assertFalse(streamReader.wasInterrupted);
+
+ // Tidy up the writer thread.
+ fifoWriter.tidyUp();
+ }
+
+ public void testStreamWrite_exceptionWhenAlreadyClosed() throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+
+ FileOutputStream fos = new FileOutputStream(fifoFile);
+ byte[] buffer = new byte[10];
+ fos.close();
+
+ try {
+ fos.write(buffer);
+ fail();
+ } catch (IOException expected) {
+ assertSame(IOException.class, expected.getClass());
+ }
+
+ fifoReader.tidyUp();
+ }
+
+ // This test fails on the RI: close() does not wake up a blocking FileInputStream.write() call.
+ public void testStreamWrite_exceptionOnCloseWhenBlocked() throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+
+ FileOutputStream fos = new FileOutputStream(fifoFile);
+ StreamWriter streamWriter = new StreamWriter(fos);
+ Thread streamWriterThread = createAndStartThread("StreamWriter", streamWriter);
+
+ // Delay until we can be fairly sure the writer thread is blocking.
+ streamWriter.waitForThreadToBlock();
+
+ // Now close the OutputStream to see what happens.
+ fos.close();
+
+ // Test for expected behavior in the writer thread.
+ waitToDie(streamWriterThread);
+ assertSame(InterruptedIOException.class, streamWriter.ioe.getClass());
+ assertFalse(streamWriter.wasInterrupted);
+
+ // Tidy up the reader thread.
+ fifoReader.tidyUp();
+ }
+
+ public void testChannelRead_exceptionWhenAlreadyClosed() throws Exception {
+ testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method.READ);
+ }
+
+ public void testChannelReadV_exceptionWhenAlreadyClosed() throws Exception {
+ testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method.READV);
+ }
+
+ private void testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method method)
+ throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+ FileInputStream fis = new FileInputStream(fifoFile);
+ FileChannel fileInputChannel = fis.getChannel();
+ fileInputChannel.close();
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+ try {
+ if (method == ChannelReader.Method.READ) {
+ fileInputChannel.read(buffer);
+ } else {
+ ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+ fileInputChannel.read(new ByteBuffer[] { buffer, buffer2});
+ }
+ fail();
+ } catch (IOException expected) {
+ assertSame(ClosedChannelException.class, expected.getClass());
+ }
+
+ fifoWriter.tidyUp();
+ }
+
+ public void testChannelRead_exceptionOnCloseWhenBlocked() throws Exception {
+ testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method.READ);
+ }
+
+ public void testChannelReadV_exceptionOnCloseWhenBlocked() throws Exception {
+ testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method.READV);
+ }
+
+ private void testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method method)
+ throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+ FileInputStream fis = new FileInputStream(fifoFile);
+ FileChannel fileInputChannel = fis.getChannel();
+
+ ChannelReader channelReader = new ChannelReader(fileInputChannel, method);
+ Thread channelReaderThread = createAndStartThread("ChannelReader", channelReader);
+
+ // Delay until we can be fairly sure the reader thread is blocking.
+ channelReader.waitForThreadToBlock();
+
+ // Now close the FileChannel to see what happens.
+ fileInputChannel.close();
+
+ // Test for expected behavior in the reader thread.
+ waitToDie(channelReaderThread);
+ assertSame(AsynchronousCloseException.class, channelReader.ioe.getClass());
+ assertFalse(channelReader.wasInterrupted);
+
+ // Tidy up the writer thread.
+ fifoWriter.tidyUp();
+ }
+
+ public void testChannelRead_exceptionOnInterrupt() throws Exception {
+ testChannelRead_exceptionOnInterrupt(ChannelReader.Method.READ);
+ }
+
+ public void testChannelReadV_exceptionOnInterrupt() throws Exception {
+ testChannelRead_exceptionOnInterrupt(ChannelReader.Method.READV);
+ }
+
+ private void testChannelRead_exceptionOnInterrupt(ChannelReader.Method method) throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+ FileChannel fileChannel = new FileInputStream(fifoFile).getChannel();
+
+ ChannelReader channelReader = new ChannelReader(fileChannel, method);
+ Thread channelReaderThread = createAndStartThread("ChannelReader", channelReader);
+
+ // Delay until we can be fairly sure the reader thread is blocking.
+ channelReader.waitForThreadToBlock();
+
+ // Now interrupt the reader thread to see what happens.
+ channelReaderThread.interrupt();
+
+ // Test for expected behavior in the reader thread.
+ waitToDie(channelReaderThread);
+ assertSame(ClosedByInterruptException.class, channelReader.ioe.getClass());
+ assertTrue(channelReader.wasInterrupted);
+
+ // Tidy up the writer thread.
+ fifoWriter.tidyUp();
+ }
+
+ public void testChannelWrite_exceptionWhenAlreadyClosed() throws Exception {
+ testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method.WRITE);
+ }
+
+ public void testChannelWriteV_exceptionWhenAlreadyClosed() throws Exception {
+ testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method.WRITEV);
+ }
+
+ private void testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method method)
+ throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+ FileChannel fileOutputChannel = new FileOutputStream(fifoFile).getChannel();
+ fileOutputChannel.close();
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+ try {
+ if (method == ChannelWriter.Method.WRITE) {
+ fileOutputChannel.write(buffer);
+ } else {
+ ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+ fileOutputChannel.write(new ByteBuffer[] { buffer, buffer2 });
+ }
+ fail();
+ } catch (IOException expected) {
+ assertSame(ClosedChannelException.class, expected.getClass());
+ }
+
+ fifoReader.tidyUp();
+ }
+
+ public void testChannelWrite_exceptionOnCloseWhenBlocked() throws Exception {
+ testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method.WRITE);
+ }
+
+ public void testChannelWriteV_exceptionOnCloseWhenBlocked() throws Exception {
+ testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method.WRITEV);
+ }
+
+ private void testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method method)
+ throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+ FileChannel fileOutputChannel = new FileOutputStream(fifoFile).getChannel();
+
+ ChannelWriter channelWriter = new ChannelWriter(fileOutputChannel, method);
+ Thread channelWriterThread = createAndStartThread("ChannelWriter", channelWriter);
+
+ // Delay until we can be fairly sure the writer thread is blocking.
+ channelWriter.waitForThreadToBlock();
+
+ // Now close the channel to see what happens.
+ fileOutputChannel.close();
+
+ // Test for expected behavior in the writer thread.
+ waitToDie(channelWriterThread);
+ // The RI throws ChannelClosedException. AsynchronousCloseException is more correct according to
+ // the docs.
+ assertSame(AsynchronousCloseException.class, channelWriter.ioe.getClass());
+ assertFalse(channelWriter.wasInterrupted);
+
+ // Tidy up the writer thread.
+ fifoReader.tidyUp();
+ }
+
+ public void testChannelWrite_exceptionOnInterrupt() throws Exception {
+ testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method.WRITE);
+ }
+
+ public void testChannelWriteV_exceptionOnInterrupt() throws Exception {
+ testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method.WRITEV);
+ }
+
+ private void testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method method) throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+
+ FileChannel fileChannel = new FileOutputStream(fifoFile).getChannel();
+ ChannelWriter channelWriter = new ChannelWriter(fileChannel, method);
+ Thread channelWriterThread = createAndStartThread("ChannelWriter", channelWriter);
+
+ // Delay until we can be fairly sure the writer thread is blocking.
+ channelWriter.waitForThreadToBlock();
+
+ // Now interrupt the writer thread to see what happens.
+ channelWriterThread.interrupt();
+
+ // Test for expected behavior in the writer thread.
+ waitToDie(channelWriterThread);
+ assertSame(ClosedByInterruptException.class, channelWriter.ioe.getClass());
+ assertTrue(channelWriter.wasInterrupted);
+
+ // Tidy up the reader thread.
+ fifoReader.tidyUp();
+ }
+
+ private static class StreamReader implements Runnable {
+
+ private final FileInputStream inputStream;
+ volatile boolean started;
+ volatile IOException ioe;
+ volatile boolean wasInterrupted;
+
+ StreamReader(FileInputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ @Override
+ public void run() {
+ byte[] buffer = new byte[10];
+ try {
+ started = true;
+ int bytesRead = inputStream.read(buffer);
+ fail("This isn't supposed to happen: read() returned: " + bytesRead);
+ } catch (IOException e) {
+ this.ioe = e;
+ }
+ wasInterrupted = Thread.interrupted();
+ }
+
+ public void waitForThreadToBlock() {
+ for (int i = 0; i < 10 && !started; i++) {
+ delay(100);
+ }
+ assertTrue(started);
+ // Just give it some more time to start blocking.
+ delay(100);
+ }
+ }
+
+ private static class StreamWriter implements Runnable {
+
+ private final FileOutputStream outputStream;
+ volatile int bytesWritten;
+ volatile IOException ioe;
+ volatile boolean wasInterrupted;
+
+ StreamWriter(FileOutputStream outputStream) {
+ this.outputStream = outputStream;
+ }
+
+ @Override
+ public void run() {
+ // Writes to FIFOs are buffered. We try to fill the buffer and induce blocking (the
+ // buffer is typically 64k).
+ byte[] buffer = new byte[10000];
+ while (true) {
+ try {
+ outputStream.write(buffer);
+ bytesWritten += buffer.length;
+ } catch (IOException e) {
+ this.ioe = e;
+ break;
+ }
+ wasInterrupted = Thread.interrupted();
+ }
+ }
+
+ public void waitForThreadToBlock() {
+ int lastCount = bytesWritten;
+ for (int i = 0; i < 10; i++) {
+ delay(500);
+ int newBytesWritten = bytesWritten;
+ if (newBytesWritten > 0 && lastCount == newBytesWritten) {
+ // The thread is probably blocking.
+ return;
+ }
+ lastCount = bytesWritten;
+ }
+ fail("Writer never started blocking. Bytes written: " + bytesWritten);
+ }
+ }
+
+ private static class ChannelReader implements Runnable {
+ enum Method {
+ READ,
+ READV,
+ }
+
+ private final FileChannel channel;
+ private final Method method;
+ volatile boolean started;
+ volatile IOException ioe;
+ volatile boolean wasInterrupted;
+
+ ChannelReader(FileChannel channel, Method method) {
+ this.channel = channel;
+ this.method = method;
+ }
+
+ @Override
+ public void run() {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+ try {
+ started = true;
+ if (method == Method.READ) {
+ channel.read(buffer);
+ } else {
+ ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+ channel.read(new ByteBuffer[] { buffer, buffer2 });
+ }
+ fail("All tests should block until an exception");
+ } catch (IOException e) {
+ this.ioe = e;
+ }
+ wasInterrupted = Thread.interrupted();
+ }
+
+ public void waitForThreadToBlock() {
+ for (int i = 0; i < 10 && !started; i++) {
+ delay(100);
+ }
+ assertTrue(started);
+ // Just give it some more time to start blocking.
+ delay(100);
+ }
+ }
+
+ private static class ChannelWriter implements Runnable {
+ enum Method {
+ WRITE,
+ WRITEV,
+ }
+
+ private final FileChannel channel;
+ private final Method method;
+ volatile int bytesWritten;
+ volatile IOException ioe;
+ volatile boolean wasInterrupted;
+
+ ChannelWriter(FileChannel channel, Method method) {
+ this.channel = channel;
+ this.method = method;
+ }
+
+ @Override
+ public void run() {
+ ByteBuffer buffer1 = ByteBuffer.allocateDirect(10000);
+ ByteBuffer buffer2 = ByteBuffer.allocateDirect(10000);
+ // Writes to FIFOs are buffered. We try to fill the buffer and induce blocking (the
+ // buffer is typically 64k).
+ while (true) {
+ // Make the buffers look non-empty.
+ buffer1.position(0).limit(buffer1.capacity());
+ buffer2.position(0).limit(buffer2.capacity());
+ try {
+ if (method == Method.WRITE) {
+ bytesWritten += channel.write(buffer1);
+ } else {
+ bytesWritten += channel.write(new ByteBuffer[]{ buffer1, buffer2 });
+ }
+ } catch (IOException e) {
+ this.ioe = e;
+ break;
+ }
+ }
+ wasInterrupted = Thread.interrupted();
+ }
+
+ public void waitForThreadToBlock() {
+ int lastCount = bytesWritten;
+ for (int i = 0; i < 10; i++) {
+ delay(500);
+ int newBytesWritten = bytesWritten;
+ if (newBytesWritten > 0 && lastCount == newBytesWritten) {
+ // The thread is probably blocking.
+ return;
+ }
+ lastCount = bytesWritten;
+ }
+ fail("Writer never started blocking. Bytes written: " + bytesWritten);
+ }
+ }
+
+ /**
+ * Opens a FIFO for writing. Exists to unblock the other end of the FIFO.
+ */
+ private static class FifoWriter extends Thread {
+
+ private final File file;
+ private FileOutputStream fos;
+
+ public FifoWriter(File file) {
+ super("FifoWriter");
+ this.file = file;
+ }
+
+ @Override
+ public void run() {
+ try {
+ fos = new FileOutputStream(file);
+ } catch (IOException ignored) {
+ }
+ }
+
+ public void tidyUp() {
+ FileIOInterruptTest.waitToDie(this);
+ closeQuietly(fos);
+ }
+ }
+
+ /**
+ * Opens a FIFO for reading. Exists to unblock the other end of the FIFO.
+ */
+ private static class FifoReader extends Thread {
+
+ private final File file;
+ private FileInputStream fis;
+
+ public FifoReader(File file) {
+ super("FifoReader");
+ this.file = file;
+ }
+
+ @Override
+ public void run() {
+ try {
+ fis = new FileInputStream(file);
+ } catch (IOException ignored) {
+ }
+ }
+
+ public void tidyUp() {
+ FileIOInterruptTest.waitToDie(this);
+ closeQuietly(fis);
+ }
+ }
+
+ private static Thread createAndStartThread(String name, Runnable runnable) {
+ Thread t = new Thread(runnable, name);
+ t.setDaemon(true);
+ t.start();
+ return t;
+ }
+
+ private static void waitToDie(Thread thread) {
+ try {
+ thread.join(5000);
+ } catch (InterruptedException ignored) {
+ }
+
+ if (thread.isAlive()) {
+ fail("Thread \"" + thread.getName() + "\" did not exit.");
+ }
+ }
+
+ private static void delay(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+}
diff --git a/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
index 6560a7b..0d0c69f 100644
--- a/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
@@ -19,7 +19,6 @@
import dalvik.annotation.BrokenTest;
import java.io.IOException;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
@@ -198,14 +197,12 @@
// expected
}
- SocketChannel channel1IP = null;
try {
- channel1IP = SocketChannel.open(null);
+ SocketChannel.open(null);
fail("Should throw an IllegalArgumentException");
} catch (IllegalArgumentException e) {
// correct
}
- assertNull(channel1IP);
}
private void ensureServerClosed() throws IOException {
@@ -355,48 +352,59 @@
isConstructorCalled = true;
}
+ @Override
public Socket socket() {
return null;
}
+ @Override
public boolean isConnected() {
return false;
}
+ @Override
public boolean isConnectionPending() {
return false;
}
+ @Override
public boolean connect(SocketAddress address) throws IOException {
return false;
}
+ @Override
public boolean finishConnect() throws IOException {
return false;
}
+ @Override
public int read(ByteBuffer target) throws IOException {
return 0;
}
+ @Override
public long read(ByteBuffer[] targets, int offset, int length)
throws IOException {
return 0;
}
+ @Override
public int write(ByteBuffer source) throws IOException {
return 0;
}
+ @Override
public long write(ByteBuffer[] sources, int offset, int length)
throws IOException {
return 0;
}
+ @Override
protected void implCloseSelectableChannel() throws IOException {
// empty
}
+ @Override
protected void implConfigureBlocking(boolean blockingMode)
throws IOException {
// empty
diff --git a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
index b45f8e1..c5f449e 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
@@ -15,6 +15,7 @@
*/
package libcore.java.nio.channels;
+import android.system.OsConstants;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
@@ -26,9 +27,8 @@
import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import libcore.io.Libcore;
-import libcore.io.OsConstants;
import junit.framework.TestCase;
+import libcore.io.Libcore;
import tests.net.StuckServer;
public class SelectorTest extends TestCase {
@@ -93,11 +93,15 @@
// http://code.google.com/p/android/issues/detail?id=15388
public void testInterrupted() throws IOException {
Selector selector = Selector.open();
+ Thread.currentThread().interrupt();
try {
- Thread.currentThread().interrupt();
int count = selector.select();
assertEquals(0, count);
+ assertTrue(Thread.currentThread().isInterrupted());
} finally {
+ // Clear the interrupted thread state so that it does not interfere with later tests.
+ Thread.interrupted();
+
selector.close();
}
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
index e66096c..e819d82 100644
--- a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
@@ -16,7 +16,19 @@
package libcore.java.nio.channels;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.ServerSocket;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
+import java.nio.channels.ClosedChannelException;
import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.UnresolvedAddressException;
+import java.util.Enumeration;
+import java.util.Set;
public class ServerSocketChannelTest extends junit.framework.TestCase {
// http://code.google.com/p/android/issues/detail?id=16579
@@ -24,11 +36,212 @@
ServerSocketChannel ssc = ServerSocketChannel.open();
try {
ssc.configureBlocking(false);
- ssc.socket().bind(null);
+ ssc.bind(null);
// Should return immediately, since we're non-blocking.
assertNull(ssc.accept());
} finally {
ssc.close();
}
}
+
+ /** Checks the state of the ServerSocketChannel and associated ServerSocket after open() */
+ public void test_open_initialState() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ try {
+ assertNull(ssc.getLocalAddress());
+
+ ServerSocket socket = ssc.socket();
+ assertFalse(socket.isBound());
+ assertFalse(socket.isClosed());
+ assertEquals(-1, socket.getLocalPort());
+ assertNull(socket.getLocalSocketAddress());
+ assertNull(socket.getInetAddress());
+ assertTrue(socket.getReuseAddress());
+
+ assertSame(ssc, socket.getChannel());
+ } finally {
+ ssc.close();
+ }
+ }
+
+ public void test_bind_unresolvedAddress() throws IOException {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ try {
+ ssc.bind(new InetSocketAddress("unresolvedname", 31415));
+ fail();
+ } catch (UnresolvedAddressException expected) {
+ }
+
+ assertNull(ssc.getLocalAddress());
+ assertTrue(ssc.isOpen());
+
+ ssc.close();
+ }
+
+ public void test_bind_nullBindsToAll() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.bind(null);
+ InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress();
+ assertTrue(boundAddress.getAddress().isAnyLocalAddress());
+ assertFalse(boundAddress.getAddress().isLinkLocalAddress());
+ assertFalse(boundAddress.getAddress().isLoopbackAddress());
+
+ // Attempt to connect to the "any" address.
+ assertTrue(canConnect(boundAddress));
+
+ // Go through all local IPs and try to connect to each in turn - all should succeed.
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ while (interfaces.hasMoreElements()) {
+ NetworkInterface nic = interfaces.nextElement();
+ Enumeration<InetAddress> inetAddresses = nic.getInetAddresses();
+ while (inetAddresses.hasMoreElements()) {
+ InetSocketAddress address =
+ new InetSocketAddress(inetAddresses.nextElement(), boundAddress.getPort());
+ assertTrue(canConnect(address));
+ }
+ }
+
+ ssc.close();
+ }
+
+ public void test_bind_loopback() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
+ InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress();
+ assertFalse(boundAddress.getAddress().isAnyLocalAddress());
+ assertFalse(boundAddress.getAddress().isLinkLocalAddress());
+ assertTrue(boundAddress.getAddress().isLoopbackAddress());
+
+ // Attempt to connect to the "loopback" address. Note: There can be several loopback
+ // addresses, such as 127.0.0.1 (IPv4) and 0:0:0:0:0:0:0:1 (IPv6) and only one will be
+ // bound.
+ InetSocketAddress loopbackAddress =
+ new InetSocketAddress(InetAddress.getLoopbackAddress(), boundAddress.getPort());
+ assertTrue(canConnect(loopbackAddress));
+
+ // Go through all local IPs and try to connect to each in turn - all should fail except
+ // for the loopback.
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ while (interfaces.hasMoreElements()) {
+ NetworkInterface nic = interfaces.nextElement();
+ Enumeration<InetAddress> inetAddresses = nic.getInetAddresses();
+ while (inetAddresses.hasMoreElements()) {
+ InetSocketAddress address =
+ new InetSocketAddress(inetAddresses.nextElement(), boundAddress.getPort());
+ if (!address.equals(loopbackAddress)) {
+ assertFalse(canConnect(address));
+ }
+ }
+ }
+
+ ssc.close();
+ }
+
+ private static boolean canConnect(InetSocketAddress address) {
+ try {
+ SocketChannel socketChannel = SocketChannel.open(address);
+ socketChannel.close();
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ public void test_supportedOptions() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ Set<SocketOption<?>> options = ssc.supportedOptions();
+
+ // Probe some values. This is not intended to be complete.
+ assertTrue(options.contains(StandardSocketOptions.SO_REUSEADDR));
+ assertFalse(options.contains(StandardSocketOptions.IP_MULTICAST_TTL));
+ }
+
+ public void test_getOption_unsupportedOption() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ try {
+ ssc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+
+ ssc.close();
+ }
+
+ public void test_getOption_afterClose() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.close();
+
+ try {
+ ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ fail();
+ } catch (ClosedChannelException expected) {}
+ }
+
+ public void test_setOption_afterClose() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.close();
+
+ try {
+ ssc.setOption(StandardSocketOptions.SO_RCVBUF, 1234);
+ fail();
+ } catch (ClosedChannelException expected) {}
+ }
+
+ public void test_getOption_SO_RCVBUF_defaults() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+
+ int value = ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertTrue(value > 0);
+ assertEquals(value, ssc.socket().getReceiveBufferSize());
+
+ ssc.close();
+ }
+
+ public void test_setOption_SO_RCVBUF_afterOpen() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+
+ trySetReceiveBufferSizeOption(ssc);
+
+ ssc.close();
+ }
+
+ private static void trySetReceiveBufferSizeOption(ServerSocketChannel ssc) throws IOException {
+ int initialValue = ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ try {
+ ssc.setOption(StandardSocketOptions.SO_RCVBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ int actualValue = ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, ssc.socket().getReceiveBufferSize());
+
+ int newBufferSize = initialValue - 1;
+ ssc.setOption(StandardSocketOptions.SO_RCVBUF, newBufferSize);
+ actualValue = ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, ssc.socket().getReceiveBufferSize());
+ }
+
+ public void test_getOption_SO_REUSEADDR_defaults() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+
+ boolean value = ssc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ assertTrue(value);
+ assertTrue(ssc.socket().getReuseAddress());
+
+ ssc.close();
+ }
+
+ public void test_setOption_SO_REUSEADDR_afterOpen() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+
+ boolean initialValue = ssc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ ssc.setOption(StandardSocketOptions.SO_REUSEADDR, !initialValue);
+ assertEquals(!initialValue, (boolean) ssc.getOption(StandardSocketOptions.SO_REUSEADDR));
+ assertEquals(!initialValue, ssc.socket().getReuseAddress());
+
+ ssc.close();
+ }
+
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
index 6ab91ab..6bba862 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
@@ -16,26 +16,39 @@
package libcore.java.nio.channels;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.net.ConnectException;
+import java.net.Socket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
-import java.nio.channels.SocketChannel;
+import java.nio.channels.UnresolvedAddressException;
+import java.util.Set;
+
import tests.io.MockOs;
-import static libcore.io.OsConstants.*;
+
+import static android.system.OsConstants.*;
public class SocketChannelTest extends junit.framework.TestCase {
+
private final MockOs mockOs = new MockOs();
- @Override public void setUp() throws Exception {
+ @Override
+ public void setUp() throws Exception {
mockOs.install();
}
- @Override protected void tearDown() throws Exception {
+ @Override
+ protected void tearDown() throws Exception {
mockOs.uninstall();
}
@@ -61,6 +74,7 @@
}
}
+ // https://code.google.com/p/android/issues/detail?id=56684
public void test_56684() throws Exception {
mockOs.enqueueFault("connect", ENETUNREACH);
@@ -78,7 +92,491 @@
try {
sc.finishConnect();
+ fail();
} catch (ClosedChannelException expected) {
}
}
+
+ /** Checks that closing a Socket's output stream also closes the Socket and SocketChannel. */
+ public void test_channelSocketOutputStreamClosureState() throws Exception {
+ ServerSocket ss = new ServerSocket(0);
+
+ SocketChannel sc = SocketChannel.open(ss.getLocalSocketAddress());
+ sc.configureBlocking(true);
+
+ Socket scSocket = sc.socket();
+ OutputStream os = scSocket.getOutputStream();
+
+ assertTrue(sc.isOpen());
+ assertFalse(scSocket.isClosed());
+
+ os.close();
+
+ assertFalse(sc.isOpen());
+ assertTrue(scSocket.isClosed());
+
+ ss.close();
+ }
+
+ /** Checks that closing a Socket's input stream also closes the Socket and SocketChannel. */
+ public void test_channelSocketInputStreamClosureState() throws Exception {
+ ServerSocket ss = new ServerSocket(0);
+
+ SocketChannel sc = SocketChannel.open(ss.getLocalSocketAddress());
+ sc.configureBlocking(true);
+
+ Socket scSocket = sc.socket();
+ InputStream is = scSocket.getInputStream();
+
+ assertTrue(sc.isOpen());
+ assertFalse(scSocket.isClosed());
+
+ is.close();
+
+ assertFalse(sc.isOpen());
+ assertTrue(scSocket.isClosed());
+
+ ss.close();
+ }
+
+ /** Checks the state of the SocketChannel and associated Socket after open() */
+ public void test_open_initialState() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ try {
+ assertNull(sc.getLocalAddress());
+
+ Socket socket = sc.socket();
+ assertFalse(socket.isBound());
+ assertFalse(socket.isClosed());
+ assertFalse(socket.isConnected());
+ assertEquals(-1, socket.getLocalPort());
+ assertTrue(socket.getLocalAddress().isAnyLocalAddress());
+ assertNull(socket.getLocalSocketAddress());
+ assertNull(socket.getInetAddress());
+ assertEquals(0, socket.getPort());
+ assertNull(socket.getRemoteSocketAddress());
+ assertFalse(socket.getReuseAddress());
+
+ assertSame(sc, socket.getChannel());
+ } finally {
+ sc.close();
+ }
+ }
+
+ public void test_bind_unresolvedAddress() throws IOException {
+ SocketChannel sc = SocketChannel.open();
+ try {
+ sc.bind(new InetSocketAddress("unresolvedname", 31415));
+ fail();
+ } catch (UnresolvedAddressException expected) {
+ }
+
+ assertNull(sc.getLocalAddress());
+ assertTrue(sc.isOpen());
+ assertFalse(sc.isConnected());
+
+ sc.close();
+ }
+
+ /** Checks that the SocketChannel and associated Socket agree on the socket state. */
+ public void test_bind_socketStateSync() throws IOException {
+ SocketChannel sc = SocketChannel.open();
+ assertNull(sc.getLocalAddress());
+
+ Socket socket = sc.socket();
+ assertNull(socket.getLocalSocketAddress());
+ assertFalse(socket.isBound());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ sc.bind(bindAddr);
+
+ InetSocketAddress actualAddr = (InetSocketAddress) sc.getLocalAddress();
+ assertEquals(actualAddr, socket.getLocalSocketAddress());
+ assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+ assertTrue(socket.isBound());
+ assertFalse(socket.isConnected());
+ assertFalse(socket.isClosed());
+
+ sc.close();
+
+ assertFalse(sc.isOpen());
+ assertTrue(socket.isClosed());
+ }
+
+ /**
+ * Checks that the SocketChannel and associated Socket agree on the socket state, even if
+ * the Socket object is requested/created after bind().
+ */
+ public void test_bind_socketObjectCreationAfterBind() throws IOException {
+ SocketChannel sc = SocketChannel.open();
+ assertNull(sc.getLocalAddress());
+
+ InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+ sc.bind(bindAddr);
+
+ // Socket object creation after bind().
+ Socket socket = sc.socket();
+ InetSocketAddress actualAddr = (InetSocketAddress) sc.getLocalAddress();
+ assertEquals(actualAddr, socket.getLocalSocketAddress());
+ assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+ assertTrue(socket.isBound());
+ assertFalse(socket.isConnected());
+ assertFalse(socket.isClosed());
+
+ sc.close();
+
+ assertFalse(sc.isOpen());
+ assertTrue(socket.isClosed());
+ }
+
+ /**
+ * Tests connect() and object state for a blocking SocketChannel. Blocking mode is the default.
+ */
+ public void test_connect_blocking() throws Exception {
+ ServerSocket ss = new ServerSocket(0);
+
+ SocketChannel sc = SocketChannel.open();
+ assertTrue(sc.isBlocking());
+
+ assertTrue(sc.connect(ss.getLocalSocketAddress()));
+
+ assertTrue(sc.socket().isBound());
+ assertTrue(sc.isConnected());
+ assertTrue(sc.socket().isConnected());
+ assertFalse(sc.socket().isClosed());
+ assertTrue(sc.isBlocking());
+
+ ss.close();
+ sc.close();
+ }
+
+ /** Tests connect() and object state for a non-blocking SocketChannel. */
+ public void test_connect_nonBlocking() throws Exception {
+ ServerSocket ss = new ServerSocket(0);
+
+ SocketChannel sc = SocketChannel.open();
+ assertTrue(sc.isBlocking());
+ sc.configureBlocking(false);
+ assertFalse(sc.isBlocking());
+
+ if (!sc.connect(ss.getLocalSocketAddress())) {
+ do {
+ assertTrue(sc.socket().isBound());
+ assertFalse(sc.isConnected());
+ assertFalse(sc.socket().isConnected());
+ assertFalse(sc.socket().isClosed());
+ } while (!sc.finishConnect());
+ }
+ assertTrue(sc.socket().isBound());
+ assertTrue(sc.isConnected());
+ assertTrue(sc.socket().isConnected());
+ assertFalse(sc.socket().isClosed());
+ assertFalse(sc.isBlocking());
+
+ ss.close();
+ sc.close();
+ }
+
+ public void test_supportedOptions() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ Set<SocketOption<?>> options = sc.supportedOptions();
+
+ // Probe some values. This is not intended to be complete.
+ assertTrue(options.contains(StandardSocketOptions.SO_REUSEADDR));
+ assertFalse(options.contains(StandardSocketOptions.IP_MULTICAST_TTL));
+ }
+
+ public void test_getOption_unsupportedOption() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ try {
+ sc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ }
+
+ sc.close();
+ }
+
+ public void test_getOption_afterClose() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.close();
+
+ try {
+ sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
+
+ public void test_setOption_afterClose() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.close();
+
+ try {
+ sc.setOption(StandardSocketOptions.SO_RCVBUF, 1234);
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
+
+ public void test_getOption_SO_RCVBUF_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ int value = sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertTrue(value > 0);
+ assertEquals(value, sc.socket().getReceiveBufferSize());
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_RCVBUF_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetReceiveBufferSizeOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetReceiveBufferSizeOption(SocketChannel sc) throws IOException {
+ int initialValue = sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ try {
+ sc.setOption(StandardSocketOptions.SO_RCVBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ int actualValue = sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, sc.socket().getReceiveBufferSize());
+
+ int newBufferSize = initialValue - 1;
+ sc.setOption(StandardSocketOptions.SO_RCVBUF, newBufferSize);
+ actualValue = sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, sc.socket().getReceiveBufferSize());
+ }
+
+ public void test_getOption_SO_SNDBUF_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ int bufferSize = sc.getOption(StandardSocketOptions.SO_SNDBUF);
+ assertTrue(bufferSize > 0);
+ assertEquals(bufferSize, sc.socket().getSendBufferSize());
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_SNDBUF_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetSendBufferSizeOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetSendBufferSizeOption(SocketChannel sc) throws IOException {
+ int initialValue = sc.getOption(StandardSocketOptions.SO_SNDBUF);
+ try {
+ sc.setOption(StandardSocketOptions.SO_SNDBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ int actualValue = sc.getOption(StandardSocketOptions.SO_SNDBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, sc.socket().getSendBufferSize());
+
+ int newValue = initialValue - 1;
+ sc.setOption(StandardSocketOptions.SO_SNDBUF, newValue);
+ actualValue = sc.getOption(StandardSocketOptions.SO_SNDBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, sc.socket().getSendBufferSize());
+ }
+
+ public void test_getOption_SO_KEEPALIVE_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ assertFalse(sc.getOption(StandardSocketOptions.SO_KEEPALIVE));
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_KEEPALIVE_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetSoKeepaliveOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetSoKeepaliveOption(SocketChannel sc) throws IOException {
+ boolean initialValue = sc.getOption(StandardSocketOptions.SO_KEEPALIVE);
+
+ sc.setOption(StandardSocketOptions.SO_KEEPALIVE, !initialValue);
+ boolean actualValue = sc.getOption(StandardSocketOptions.SO_KEEPALIVE);
+ assertEquals(!initialValue, actualValue);
+ }
+
+ public void test_setOption_SO_KEEPALIVE_afterBind() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.bind(null);
+
+ trySetSoKeepaliveOption(sc);
+
+ sc.close();
+ }
+
+ public void test_getOption_IP_TOS_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ int value = sc.getOption(StandardSocketOptions.IP_TOS);
+ assertEquals(0, value);
+ assertEquals(value, sc.socket().getTrafficClass());
+
+ sc.close();
+ }
+
+ public void test_setOption_IP_TOS_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetTosOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetTosOption(SocketChannel sc) throws IOException {
+ int initialValue = sc.getOption(StandardSocketOptions.IP_TOS);
+ try {
+ sc.setOption(StandardSocketOptions.IP_TOS, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ assertEquals(initialValue, (int) sc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(initialValue, sc.socket().getTrafficClass());
+
+ try {
+ sc.setOption(StandardSocketOptions.IP_TOS, 256);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ assertEquals(initialValue, (int) sc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(initialValue, sc.socket().getTrafficClass());
+
+ int newValue = (initialValue + 1) % 255;
+ sc.setOption(StandardSocketOptions.IP_TOS, newValue);
+ assertEquals(newValue, (int) sc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(newValue, sc.socket().getTrafficClass());
+ }
+
+ public void test_setOption_IP_TOS_afterBind() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.bind(null);
+
+ trySetTosOption(sc);
+
+ sc.close();
+ }
+
+ public void test_getOption_SO_LINGER_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ int value = sc.getOption(StandardSocketOptions.SO_LINGER);
+ assertTrue(value < 0);
+ assertEquals(value, sc.socket().getSoLinger());
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_LINGER_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetLingerOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetLingerOption(SocketChannel sc) throws IOException {
+ int initialValue = sc.getOption(StandardSocketOptions.SO_LINGER);
+ // Any negative value disables the setting, -1 is used to report SO_LINGER being disabled.
+ sc.setOption(StandardSocketOptions.SO_LINGER, -2);
+ int soLingerDisabled = -1;
+ assertEquals(soLingerDisabled, (int) sc.getOption(StandardSocketOptions.SO_LINGER));
+ assertEquals(soLingerDisabled, sc.socket().getSoLinger());
+
+ sc.setOption(StandardSocketOptions.SO_LINGER, 65536);
+ assertEquals(65535, (int) sc.getOption(StandardSocketOptions.SO_LINGER));
+ assertEquals(65535, sc.socket().getSoLinger());
+
+ int newValue = (initialValue + 1) % 65535;
+ sc.setOption(StandardSocketOptions.SO_LINGER, newValue);
+ assertEquals(newValue, (int) sc.getOption(StandardSocketOptions.SO_LINGER));
+ assertEquals(newValue, sc.socket().getSoLinger());
+ }
+
+ public void test_setOption_SO_LINGER_afterBind() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.bind(null);
+
+ trySetLingerOption(sc);
+
+ sc.close();
+ }
+
+ public void test_getOption_SO_REUSEADDR_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ boolean value = sc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ assertFalse(value);
+ assertFalse(sc.socket().getReuseAddress());
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_REUSEADDR_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ boolean initialValue = sc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ sc.setOption(StandardSocketOptions.SO_REUSEADDR, !initialValue);
+ assertEquals(!initialValue, (boolean) sc.getOption(StandardSocketOptions.SO_REUSEADDR));
+ assertEquals(!initialValue, sc.socket().getReuseAddress());
+
+ sc.close();
+ }
+
+ public void test_getOption_TCP_NODELAY_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ boolean value = sc.getOption(StandardSocketOptions.TCP_NODELAY);
+ assertFalse(value);
+ assertFalse(sc.socket().getTcpNoDelay());
+
+ sc.close();
+ }
+
+ public void test_setOption_TCP_NODELAY_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetNoDelay(sc);
+
+ sc.close();
+ }
+
+ private static void trySetNoDelay(SocketChannel sc) throws IOException {
+ boolean initialValue = sc.getOption(StandardSocketOptions.TCP_NODELAY);
+ sc.setOption(StandardSocketOptions.TCP_NODELAY, !initialValue);
+ assertEquals(!initialValue, (boolean) sc.getOption(StandardSocketOptions.TCP_NODELAY));
+ assertEquals(!initialValue, sc.socket().getTcpNoDelay());
+ }
+
+ public void test_setOption_TCP_NODELAY_afterBind() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.bind(null);
+
+ trySetNoDelay(sc);
+
+ sc.close();
+ }
+
}
diff --git a/luni/src/test/java/libcore/java/security/KeyStoreTest.java b/luni/src/test/java/libcore/java/security/KeyStoreTest.java
index ddee6ce..47aa72a 100644
--- a/luni/src/test/java/libcore/java/security/KeyStoreTest.java
+++ b/luni/src/test/java/libcore/java/security/KeyStoreTest.java
@@ -170,7 +170,8 @@
// Don't bother testing BC on RI
// TODO enable AndroidKeyStore when CTS can set up the keystore
return (StandardNames.IS_RI && ks.getProvider().getName().equals("BC"))
- || "AndroidKeyStore".equalsIgnoreCase(ks.getType());
+ || "AndroidKeyStore".equalsIgnoreCase(ks.getType())
+ || "TimaKeyStore".equalsIgnoreCase(ks.getType());
}
private static boolean isNullPasswordAllowed(KeyStore ks) {
diff --git a/luni/src/test/java/libcore/java/security/MessageDigestTest.java b/luni/src/test/java/libcore/java/security/MessageDigestTest.java
index 1370287..ad410e4 100644
--- a/luni/src/test/java/libcore/java/security/MessageDigestTest.java
+++ b/luni/src/test/java/libcore/java/security/MessageDigestTest.java
@@ -24,6 +24,11 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
public final class MessageDigestTest extends TestCase {
@@ -239,4 +244,33 @@
return buf.toString();
}
+ private final int THREAD_COUNT = 10;
+
+ public void testMessageDigest_MultipleThreads_Misuse() throws Exception {
+ ExecutorService es = Executors.newFixedThreadPool(THREAD_COUNT);
+
+ final CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
+ final MessageDigest md = MessageDigest.getInstance("SHA-256");
+ final byte[] message = new byte[64];
+
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ es.submit(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ // Try to make sure all the threads are ready first.
+ latch.countDown();
+ latch.await();
+
+ for (int j = 0; j < 100; j++) {
+ md.update(message);
+ md.digest();
+ }
+
+ return null;
+ }
+ });
+ }
+ es.shutdown();
+ assertTrue("Test should not timeout", es.awaitTermination(1, TimeUnit.MINUTES));
+ }
}
diff --git a/luni/src/test/java/libcore/java/security/MockPrivateKey.java b/luni/src/test/java/libcore/java/security/MockPrivateKey.java
new file mode 100644
index 0000000..e5ac797
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/MockPrivateKey.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 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.java.security;
+
+import java.security.PrivateKey;
+
+/**
+ * A mock PrivateKey class used for testing.
+ */
+@SuppressWarnings("serial")
+public class MockPrivateKey implements PrivateKey {
+ @Override
+ public String getAlgorithm() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public String getFormat() {
+ return "MOCK";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+}
diff --git a/luni/src/test/java/libcore/java/security/MockPrivateKey2.java b/luni/src/test/java/libcore/java/security/MockPrivateKey2.java
new file mode 100644
index 0000000..a1c02c9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/MockPrivateKey2.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 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.java.security;
+
+import java.security.PrivateKey;
+
+/**
+ * A mock PrivateKey class used for testing.
+ */
+@SuppressWarnings("serial")
+public class MockPrivateKey2 implements PrivateKey {
+ @Override
+ public String getAlgorithm() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public String getFormat() {
+ return "MOCK";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+}
diff --git a/luni/src/test/java/libcore/java/security/MockPublicKey.java b/luni/src/test/java/libcore/java/security/MockPublicKey.java
new file mode 100644
index 0000000..130b461
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/MockPublicKey.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 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.java.security;
+
+import java.security.PublicKey;
+
+/**
+ * A mock PublicKey class used for testing.
+ */
+@SuppressWarnings("serial")
+public class MockPublicKey implements PublicKey {
+ @Override
+ public String getAlgorithm() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public String getFormat() {
+ return "MOCK";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+}
diff --git a/luni/src/test/java/libcore/java/security/MockSignatureSpi.java b/luni/src/test/java/libcore/java/security/MockSignatureSpi.java
new file mode 100644
index 0000000..6017547
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/MockSignatureSpi.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 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.java.security;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+
+/**
+ * Mock SignatureSpi used by {@link SignatureTest}.
+ */
+public class MockSignatureSpi extends SignatureSpi {
+ public static class SpecificKeyTypes extends MockSignatureSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockPrivateKey)) {
+ throw new InvalidKeyException("Must be MockPrivateKey!");
+ }
+ }
+ }
+
+ public static class SpecificKeyTypes2 extends MockSignatureSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockPrivateKey2)) {
+ throw new InvalidKeyException("Must be MockPrivateKey2!");
+ }
+ }
+ }
+
+ public static class AllKeyTypes extends MockSignatureSpi {
+ }
+
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineInitVerify(java.security.PublicKey)
+ */
+ @Override
+ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineInitSign(java.security.PrivateKey)
+ */
+ @Override
+ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+ checkKeyType(privateKey);
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineUpdate(byte)
+ */
+ @Override
+ protected void engineUpdate(byte b) throws SignatureException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineSign()
+ */
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineVerify(byte[])
+ */
+ @Override
+ protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineSetParameter(java.lang.String, java.lang.Object)
+ */
+ @Override
+ protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineGetParameter(java.lang.String)
+ */
+ @Override
+ protected Object engineGetParameter(String param) throws InvalidParameterException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.SignatureSpi#engineUpdate(byte[], int, int)
+ */
+ @Override
+ protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+}
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index 1027dd4..5e02f10 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -18,9 +18,11 @@
import java.math.BigInteger;
import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
@@ -38,10 +40,197 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
public class SignatureTest extends TestCase {
+ private static abstract class MockProvider extends Provider {
+ public MockProvider(String name) {
+ super(name, 1.0, "Mock provider used for testing");
+ setup();
+ }
+
+ public abstract void setup();
+ }
+
+ public void testSignature_getInstance_SuppliedProviderNotRegistered_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ {
+ Signature s = Signature.getInstance("FOO", mockProvider);
+ s.initSign(new MockPrivateKey());
+ assertEquals(mockProvider, s.getProvider());
+ }
+ }
+
+ public void testSignature_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ {
+ Provider mockProvider2 = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+ }
+ };
+ Signature s = Signature.getInstance("FOO", mockProvider2);
+ assertEquals(mockProvider2, s.getProvider());
+ }
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
+ public void testSignature_getInstance_DelayedInitialization_KeyType() throws Exception {
+ Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.SpecificKeyTypes.class.getName());
+ put("Signature.FOO SupportedKeyClasses", MockPrivateKey.class.getName());
+ }
+ };
+ Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.SpecificKeyTypes2.class.getName());
+ put("Signature.FOO SupportedKeyClasses", MockPrivateKey2.class.getName());
+ }
+ };
+ Provider mockProviderAll = new MockProvider("MockProviderAll") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderSpecific);
+ Security.addProvider(mockProviderSpecific2);
+ Security.addProvider(mockProviderAll);
+
+ try {
+ {
+ Signature s = Signature.getInstance("FOO");
+ s.initSign(new MockPrivateKey());
+ assertEquals(mockProviderSpecific, s.getProvider());
+
+ try {
+ s.initSign(new MockPrivateKey2());
+ assertEquals(mockProviderSpecific2, s.getProvider());
+ if (StandardNames.IS_RI) {
+ fail("RI was broken before; fix tests now that it works!");
+ }
+ } catch (InvalidKeyException e) {
+ if (!StandardNames.IS_RI) {
+ fail("Non-RI should select the right provider");
+ }
+ }
+ }
+
+ {
+ Signature s = Signature.getInstance("FOO");
+ s.initSign(new PrivateKey() {
+ @Override
+ public String getAlgorithm() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public String getFormat() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+ });
+ assertEquals(mockProviderAll, s.getProvider());
+ }
+
+ {
+ Signature s = Signature.getInstance("FOO");
+ assertEquals(mockProviderSpecific, s.getProvider());
+ }
+ } finally {
+ Security.removeProvider(mockProviderSpecific.getName());
+ Security.removeProvider(mockProviderSpecific2.getName());
+ Security.removeProvider(mockProviderAll.getName());
+ }
+ }
+
+ private static class MySignature extends Signature {
+ protected MySignature(String algorithm) {
+ super(algorithm);
+ }
+
+ @Override
+ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineUpdate(byte b) throws SignatureException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineSetParameter(String param, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected Object engineGetParameter(String param) throws InvalidParameterException {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public void testSignature_getProvider_Subclass() throws Exception {
+ Provider mockProviderNonSpi = new MockProvider("MockProviderNonSpi") {
+ public void setup() {
+ put("Signature.FOO", MySignature.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderNonSpi);
+
+ try {
+ Signature s = new MySignature("FOO");
+ assertNull(s.getProvider());
+ } finally {
+ Security.removeProvider(mockProviderNonSpi.getName());
+ }
+ }
// 20 bytes for DSA
private final byte[] DATA = new byte[20];
@@ -140,6 +329,8 @@
// http://code.google.com/p/android/issues/detail?id=34933
sig.verify(signature);
}
+
+ testSignature_MultipleThreads_Misuse(sig);
}
private static final byte[] PK_BYTES = hexToBytes(
@@ -1426,4 +1617,33 @@
assertNotNull(oid, signature);
assertEquals(oid, signature.getAlgorithm());
}
+
+ private final int THREAD_COUNT = 10;
+
+ private void testSignature_MultipleThreads_Misuse(final Signature s) throws Exception {
+ ExecutorService es = Executors.newFixedThreadPool(THREAD_COUNT);
+
+ final CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
+ final byte[] message = new byte[64];
+
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ es.submit(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ // Try to make sure all the threads are ready first.
+ latch.countDown();
+ latch.await();
+
+ for (int j = 0; j < 100; j++) {
+ s.update(message);
+ s.sign();
+ }
+
+ return null;
+ }
+ });
+ }
+ es.shutdown();
+ assertTrue("Test should not timeout", es.awaitTermination(1, TimeUnit.MINUTES));
+ }
}
diff --git a/luni/src/test/java/libcore/java/sql/OldTimestampTest.java b/luni/src/test/java/libcore/java/sql/OldTimestampTest.java
deleted file mode 100644
index ab2034b..0000000
--- a/luni/src/test/java/libcore/java/sql/OldTimestampTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.java.sql;
-
-import java.sql.Timestamp;
-import java.util.TimeZone;
-import junit.framework.TestCase;
-
-public final class OldTimestampTest extends TestCase {
-
- public void test_toString() {
- TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
-
- Timestamp t1 = new Timestamp(Long.MIN_VALUE);
- assertEquals("292278994-08-17 07:12:55.192", t1.toString());
-
- Timestamp t2 = new Timestamp(Long.MIN_VALUE + 1);
- assertEquals("292278994-08-17 07:12:55.193", t2.toString());
-
- Timestamp t3 = new Timestamp(Long.MIN_VALUE + 807);
- assertEquals("292278994-08-17 07:12:55.999", t3.toString());
-
- Timestamp t4 = new Timestamp(Long.MIN_VALUE + 808);
- assertEquals("292269055-12-02 16:47:05.0", t4.toString());
- }
-}
diff --git a/luni/src/test/java/libcore/java/sql/TimestampTest.java b/luni/src/test/java/libcore/java/sql/TimestampTest.java
new file mode 100644
index 0000000..2985848
--- /dev/null
+++ b/luni/src/test/java/libcore/java/sql/TimestampTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.java.sql;
+
+import java.sql.Timestamp;
+import java.util.TimeZone;
+import junit.framework.TestCase;
+
+public final class TimestampTest extends TestCase {
+
+ public void testToString() {
+ // Timestamp uses the current default timezone in toString() to convert to
+ // human-readable strings.
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ try {
+ Timestamp t1 = new Timestamp(Long.MIN_VALUE);
+ assertEquals("292278994-08-17 07:12:55.192", t1.toString());
+
+ Timestamp t2 = new Timestamp(Long.MIN_VALUE + 1);
+ assertEquals("292278994-08-17 07:12:55.193", t2.toString());
+
+ Timestamp t3 = new Timestamp(Long.MIN_VALUE + 807);
+ assertEquals("292278994-08-17 07:12:55.999", t3.toString());
+
+ Timestamp t4 = new Timestamp(Long.MIN_VALUE + 808);
+ assertEquals("292269055-12-02 16:47:05.0", t4.toString());
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ }
+ }
+
+ public void testValueOf() {
+ // Timestamp uses the current default timezone in valueOf(String) to convert
+ // from human-readable strings.
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
+ try {
+ Timestamp t1 = Timestamp.valueOf("2001-12-31 21:45:57.123456789");
+ assertEquals(1009835157000L + 123456789 / 1000000, t1.getTime());
+ assertEquals(123456789, t1.getNanos());
+
+ Timestamp t2 = Timestamp.valueOf("2001-01-02 01:05:07.123");
+ assertEquals(978397507000L + 123000000 / 1000000, t2.getTime());
+ assertEquals(123000000, t2.getNanos());
+
+ Timestamp t3 = Timestamp.valueOf("2001-01-02 01:05:07");
+ assertEquals(978397507000L, t3.getTime());
+ assertEquals(0, t3.getNanos());
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ }
+ }
+
+ public void testValueOfInvalid() {
+ try {
+ Timestamp.valueOf("");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("+2001-12-31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001-+12-31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001-12-+31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("-2001-12-31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001--12-31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001-12--31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001--");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001--31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("-12-31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("-12-");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("--31");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001-12-31 21:45:57.+12345678");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001-12-31 21:45:57.-12345678");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ Timestamp.valueOf("2001-12-31 21:45:57.1234567891");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+ }
+
+}
diff --git a/luni/src/test/java/libcore/java/text/CollatorTest.java b/luni/src/test/java/libcore/java/text/CollatorTest.java
index 0a61f04..0f65b20 100644
--- a/luni/src/test/java/libcore/java/text/CollatorTest.java
+++ b/luni/src/test/java/libcore/java/text/CollatorTest.java
@@ -88,7 +88,7 @@
}
public void testEqualsObject() throws ParseException {
- String rule = "< a < b < c < d < e";
+ String rule = "&9 < a < b < c < d < e";
RuleBasedCollator coll = new RuleBasedCollator(rule);
assertEquals(Collator.TERTIARY, coll.getStrength());
@@ -109,7 +109,7 @@
// Regression test for HARMONY-1352, that doesn't get run in the harmony test suite because
// of an earlier failure.
try {
- new RuleBasedCollator("< a< b< c< d").getCollationElementIterator((CharacterIterator) null);
+ new RuleBasedCollator("&9 < a< b< c< d").getCollationElementIterator((CharacterIterator) null);
fail("NullPointerException expected");
} catch (NullPointerException expected) {
}
@@ -139,7 +139,7 @@
}
public void testGetCollationElementIteratorString_de_DE() throws Exception {
- assertGetCollationElementIteratorString(new Locale("de", "DE", ""), "\u00e6b", 0, 1, 1, 1, 1, 2);
+ assertGetCollationElementIteratorString(new Locale("de", "DE", ""), "\u00e6b", 0, 1, 1, 2);
}
public void testGetCollationElementIteratorCharacterIterator_es() throws Exception {
@@ -147,6 +147,6 @@
}
public void testGetCollationElementIteratorCharacterIterator_de_DE() throws Exception {
- assertGetCollationElementIteratorCharacterIterator(new Locale("de", "DE", ""), "\u00e6b", 0, 1, 1, 1, 1, 2);
+ assertGetCollationElementIteratorCharacterIterator(new Locale("de", "DE", ""), "\u00e6b", 0, 1, 1, 2);
}
}
diff --git a/luni/src/test/java/libcore/java/text/NumberFormatTest.java b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
index 32307b7..5862a6c 100644
--- a/luni/src/test/java/libcore/java/text/NumberFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
@@ -66,16 +66,16 @@
public void test_getIntegerInstance_ar() throws Exception {
NumberFormat numberFormat = NumberFormat.getNumberInstance(new Locale("ar"));
- assertEquals("#0.###;#0.###-", ((DecimalFormat) numberFormat).toPattern());
+ assertEquals("#,##0.###", ((DecimalFormat) numberFormat).toPattern());
NumberFormat integerFormat = NumberFormat.getIntegerInstance(new Locale("ar"));
- assertEquals("#0;#0-", ((DecimalFormat) integerFormat).toPattern());
+ assertEquals("#,##0", ((DecimalFormat) integerFormat).toPattern());
}
public void test_numberLocalization() throws Exception {
Locale arabic = new Locale("ar");
NumberFormat nf = NumberFormat.getNumberInstance(arabic);
assertEquals('\u0660', new DecimalFormatSymbols(arabic).getZeroDigit());
- assertEquals("١٢٣٤٥٦٧٨٩٠", nf.format(1234567890));
+ assertEquals("١٬٢٣٤٬٥٦٧٬٨٩٠", nf.format(1234567890));
}
// Formatting percentages is confusing but deliberate.
diff --git a/luni/src/test/java/libcore/java/text/OldCollationKeyTest.java b/luni/src/test/java/libcore/java/text/OldCollationKeyTest.java
index 1fc8264..eea5246 100644
--- a/luni/src/test/java/libcore/java/text/OldCollationKeyTest.java
+++ b/luni/src/test/java/libcore/java/text/OldCollationKeyTest.java
@@ -24,7 +24,7 @@
public class OldCollationKeyTest extends junit.framework.TestCase {
- public void test_toByteArray() {
+ public void test_toByteArray() throws ParseException {
// Test for method byte [] java.text.CollationKey.toByteArray()
Collator collator = Collator.getInstance();
collator.setStrength(Collator.PRIMARY);
@@ -32,12 +32,7 @@
byte[] bytes = key1.toByteArray();
assertTrue("Not enough bytes", bytes.length >= 3);
- try {
- collator = new RuleBasedCollator("= 1 , 2 ; 3 , 4 < 5 ; 6 , 7");
- } catch (ParseException e) {
- fail("ParseException");
- return;
- }
+ collator = new RuleBasedCollator("&0 = 1 , 2 ; 3 , 4 < 5 ; 6 , 7");
/*
* CollationElementIterator it =
* ((RuleBasedCollator)collator).getCollationElementIterator("1234567");
diff --git a/luni/src/test/java/libcore/java/text/OldNumberFormatTest.java b/luni/src/test/java/libcore/java/text/OldNumberFormatTest.java
index b7ae098..356d0a9 100644
--- a/luni/src/test/java/libcore/java/text/OldNumberFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/OldNumberFormatTest.java
@@ -50,10 +50,14 @@
Locale arLocale = new Locale("ar", "AE");
format = (DecimalFormat) NumberFormat.getIntegerInstance(arLocale);
- assertEquals("#0;#0-", format.toPattern());
- assertEquals("\u0666-", format.format(-6));
- assertEquals(new Long(-36), format.parse("36-"));
- assertEquals(new Long(-36), format.parseObject("36-"));
+ assertEquals("#,##0", format.toPattern());
+ assertEquals("\u0666\u0667", format.format(67));
+
+ assertEquals("\u200f-\u0666", format.format(-6));
+ assertEquals(-36L, format.parse("-36"));
+
+ // New Arabic formats do not support '-' to right of digits.
+ assertEquals(36L, format.parseObject("36-"));
assertEquals(0, format.getMaximumFractionDigits());
assertTrue(format.isParseIntegerOnly());
}
diff --git a/luni/src/test/java/libcore/java/util/CurrencyTest.java b/luni/src/test/java/libcore/java/util/CurrencyTest.java
index 61a22fd..fb7fbf6 100644
--- a/luni/src/test/java/libcore/java/util/CurrencyTest.java
+++ b/luni/src/test/java/libcore/java/util/CurrencyTest.java
@@ -67,6 +67,13 @@
assertEquals(-1, Currency.getInstance("XXX").getDefaultFractionDigits());
}
+ public void test_getNumericCode() throws Exception {
+ assertEquals(840, Currency.getInstance("USD").getNumericCode());
+ assertEquals(826, Currency.getInstance("GBP").getNumericCode());
+ assertEquals(999, Currency.getInstance("XXX").getNumericCode());
+ assertEquals(0, Currency.getInstance("XFU").getNumericCode());
+ }
+
// http://code.google.com/p/android/issues/detail?id=38622
public void test_getSymbol_38622() throws Exception {
// The CLDR data had the Portuguese symbol for "EUR" in pt, not in pt_PT.
diff --git a/luni/src/test/java/libcore/java/util/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java
index 9b67025..025a3ba 100644
--- a/luni/src/test/java/libcore/java/util/LocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/LocaleTest.java
@@ -22,9 +22,15 @@
import java.text.DateFormatSymbols;
import java.text.NumberFormat;
import java.util.Calendar;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.IllformedLocaleException;
import java.util.Locale;
+import java.util.Map;
import java.util.MissingResourceException;
+import java.util.Set;
+import java.util.TreeMap;
+import libcore.icu.ICU;
public class LocaleTest extends junit.framework.TestCase {
// http://b/2611311; if there's no display language/country/variant, use the raw codes.
@@ -64,17 +70,33 @@
assertEquals("Deutsch", Locale.GERMAN.getDisplayLanguage(Locale.GERMAN));
}
+ // https://b/issue?id=13790528
+ public void test_getDisplayName_withScriptsAndVariants() throws Exception {
+ // Script + Country.
+ assertEquals("Chinese (Traditional Han,China)",
+ Locale.forLanguageTag("zh-Hant-CN").getDisplayName(Locale.US));
+ // Script + Variant.
+ assertEquals("Chinese (Traditional Han,VARIANT)",
+ Locale.forLanguageTag("zh-Hant-VARIANT").getDisplayName(Locale.US));
+ // Country + Variant.
+ assertEquals("Chinese (China,VARIANT)",
+ Locale.forLanguageTag("zh-CN-VARIANT").getDisplayName(Locale.US));
+ // Script + Country + variant.
+ assertEquals("Chinese (Traditional Han,China,VARIANT)",
+ Locale.forLanguageTag("zh-Hant-CN-VARIANT").getDisplayName(Locale.US));
+ }
+
public void test_getDisplayCountry_8870289() throws Exception {
assertEquals("Hong Kong", new Locale("", "HK").getDisplayCountry(Locale.US));
assertEquals("Macau", new Locale("", "MO").getDisplayCountry(Locale.US));
assertEquals("Palestine", new Locale("", "PS").getDisplayCountry(Locale.US));
- assertEquals("Cocos [Keeling] Islands", new Locale("", "CC").getDisplayCountry(Locale.US));
- assertEquals("Congo [DRC]", new Locale("", "CD").getDisplayCountry(Locale.US));
- assertEquals("Congo [Republic]", new Locale("", "CG").getDisplayCountry(Locale.US));
- assertEquals("Falkland Islands [Islas Malvinas]", new Locale("", "FK").getDisplayCountry(Locale.US));
- assertEquals("Macedonia [FYROM]", new Locale("", "MK").getDisplayCountry(Locale.US));
- assertEquals("Myanmar [Burma]", new Locale("", "MM").getDisplayCountry(Locale.US));
+ assertEquals("Cocos (Keeling) Islands", new Locale("", "CC").getDisplayCountry(Locale.US));
+ assertEquals("Congo (DRC)", new Locale("", "CD").getDisplayCountry(Locale.US));
+ assertEquals("Congo (Republic)", new Locale("", "CG").getDisplayCountry(Locale.US));
+ assertEquals("Falkland Islands (Islas Malvinas)", new Locale("", "FK").getDisplayCountry(Locale.US));
+ assertEquals("Macedonia (FYROM)", new Locale("", "MK").getDisplayCountry(Locale.US));
+ assertEquals("Myanmar (Burma)", new Locale("", "MM").getDisplayCountry(Locale.US));
assertEquals("Taiwan", new Locale("", "TW").getDisplayCountry(Locale.US));
}
@@ -177,7 +199,6 @@
assertEquals("eng", new Locale("en", "CA").getISO3Language());
assertEquals("eng", new Locale("en", "XX").getISO3Language());
}
- /*
public void test_serializeExtensions() {
Map<Character, String> extensions = new TreeMap<Character, String>();
@@ -262,7 +283,6 @@
assertEquals("type1-type1", keywords.get("k1"));
assertEquals("type2", keywords.get("k2"));
}
- */
public void test_Builder_setLanguage() {
Locale.Builder b = new Locale.Builder();
@@ -1131,5 +1151,35 @@
b.setLanguage("en").setExtension('x', "fooo-baar");
assertEquals("en__#x-fooo-baar", b.build().toString());
}
-}
+ // Tests cases where our "guess" for the output size is incorrect.
+ //
+ // https://b.corp.google.com/issue?id=13414549
+ public void test_toLanguageTag_largerTag() {
+ Locale posix = new Locale.Builder()
+ .setLanguage("en").setRegion("US").setVariant("POSIX")
+ .build();
+ assertEquals("en-US-u-va-posix", posix.toLanguageTag());
+ }
+
+ public void test_setDefault_setsICUDefaultLocale() {
+ Locale l = Locale.getDefault();
+
+ try {
+ Locale.setDefault(Locale.GERMANY);
+ assertEquals("de_DE", ICU.getDefaultLocale());
+
+ try {
+ Locale.setDefault(null);
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals(Locale.GERMANY, Locale.getDefault());
+ }
+
+ Locale.setDefault(new Locale("bogus", "LOCALE"));
+ assertEquals("bogus__LOCALE", ICU.getDefaultLocale());
+ } finally {
+ Locale.setDefault(l);
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
index 713e1b5..93bd028 100644
--- a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
@@ -92,7 +92,7 @@
public void test_getDisplayNameLjava_util_Locale() {
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
assertEquals("Pacific Standard Time", tz.getDisplayName(new Locale("US")));
- assertEquals("heure normale du Pacifique", tz.getDisplayName(Locale.FRANCE));
+ assertEquals("heure normale du Pacifique nord-américain", tz.getDisplayName(Locale.FRANCE));
}
public void test_getDisplayNameZI() {
@@ -113,7 +113,7 @@
// ICU zone/root.txt patched to allow metazone names.
assertEquals("PST", tz.getDisplayName(false, 0, Locale.FRANCE));
assertEquals("heure avanc\u00e9e du Pacifique", tz.getDisplayName(true, 1, Locale.FRANCE));
- assertEquals("heure normale du Pacifique", tz.getDisplayName(false, 1, Locale.FRANCE));
+ assertEquals("heure normale du Pacifique nord-américain", tz.getDisplayName(false, 1, Locale.FRANCE));
}
public void test_getID() {
diff --git a/luni/src/test/java/libcore/java/util/UUIDTest.java b/luni/src/test/java/libcore/java/util/UUIDTest.java
new file mode 100644
index 0000000..61e4ae0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/UUIDTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 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.java.util;
+
+import java.util.UUID;
+import junit.framework.TestCase;
+
+// There are more tests in the harmony suite:
+// harmony-tests/src/test/java/org/apache/harmony/tests/java/util/UUIDTest.java
+public class UUIDTest extends TestCase {
+
+ public void testFromStringInvalidValues() {
+ try {
+ UUID.fromString("+f81d4fae-7dec-11d0-a765-00a0c91e6bf6");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ UUID.fromString("f81d4fae-+7dec-11d0-a765-00a0c91e6bf6");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ UUID.fromString("f81d4fae-7dec-+11d0-a765-00a0c91e6bf6");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ UUID.fromString("f81d4fae-7dec-11d0-+a765-00a0c91e6bf6");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ UUID.fromString("f81d4fae-7dec-11d0-a765-+00a0c91e6bf6");
+ fail();
+ } catch (IllegalArgumentException expected) { }
+ }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/jar/DalvikExecTest.java b/luni/src/test/java/libcore/java/util/jar/DalvikExecTest.java
deleted file mode 100644
index 021cd3f..0000000
--- a/luni/src/test/java/libcore/java/util/jar/DalvikExecTest.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2008 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.java.util.jar;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import junit.framework.TestCase;
-import libcore.io.Streams;
-import static tests.support.Support_Exec.execAndGetOutput;
-import tests.support.resource.Support_Resources;
-
-
-public class DalvikExecTest extends TestCase {
-
- String execDalvik1(String classpath, String mainClass, String arg1)
- throws IOException, InterruptedException {
- ProcessBuilder builder = new ProcessBuilder();
-
- File dalvikvm = new File("/system/bin/dalvikvm");
- if (dalvikvm.exists()) {
- builder.command().add(dalvikvm.getPath());
- } else {
- builder.command().add("dalvikvm"); // for host mode, assume dalvikvm is on the path
- }
-
- builder.command().add("-Duser.language=en");
- builder.command().add("-Duser.region=US");
- builder.command().add("-Xbootclasspath:" + System.getProperty("java.boot.class.path"));
- builder.command().add("-classpath");
- builder.command().add(classpath);
- builder.command().add(mainClass);
-
- if (arg1 != null) {
- builder.command().add(arg1);
- }
-
- // Create a writable dalvik-cache under ANDROID_DATA.
- // The default dalvik-cache is only writable by the system user (and root).
- String tmp = System.getProperty("java.io.tmpdir");
- builder.environment().put("ANDROID_DATA", tmp);
- new File(tmp, "dalvik-cache").mkdir();
-
- return execAndGetOutput(builder);
- }
-
- String execDalvik (String classpath, String mainClass)
- throws IOException, InterruptedException {
- return execDalvik1(classpath, mainClass, null);
- }
-
- // Execute an existing JAR on dalvikvm using -classpath option.",
- public void test_execExistingJar () throws IOException, InterruptedException {
- String res;
- File jarFile;
- if (System.getProperty("java.vendor").contains("Android")) {
- //
- // Test against Android:
- //
- File tempDir = Support_Resources.createTempFolder();
- jarFile = Support_Resources.copyFile(
- tempDir, null, "cts_dalvikExecTest.jar" );
- res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.HelloWorld");
- assertEquals("Hello Android World!", "Hello Android World!\n", res);
-
- res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.ResourceDumper");
- assertTrue("Android Resource Dumper started",
- res.contains("Android Resource Dumper started"));
- assertTrue("This Resource contains some text.",
- res.contains("This Resource contains some text."));
- } else {
- //
- // Test against RI:
- //
- // Do nothing!
- }
- }
-
- // Create a temp file, fill it with contents according to Dalvik JAR format, and execute it on dalvikvm using -classpath option.",
- public void test_execCreatedJar () throws IOException, InterruptedException {
- File jarFile = File.createTempFile("cts_dalvikExecTest_", ".jar");
- jarFile.deleteOnExit();
-
- // Create a JAR output stream on the temp file:
- JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(jarFile));
-
- // Define the entry for the classes.dex:
- jarOut.putNextEntry(new JarEntry("classes.dex"));
-
- // Fill in the classes.dex contents, i.e. the Dalvik executable code:
- // (See below for the detailed source code contents.)
- Streams.copy(Support_Resources.getResourceStream("cts_dalvikExecTest_classes.dex"), jarOut);
-
- // Now add a resource file:
- //
- jarOut.putNextEntry(new JarEntry("dalvikExecTest/myResource"));
- jarOut.write("This Resource contains some text.".getBytes());
-
- // Close the stream to the completed JAR file.
- jarOut.close();
-
- // The resulting JAR file contains the classes listed at the end of this text,
- // like the 'cts_dalvikExecTest.jar' as part of the resources, too.
-
- String res;
-
- res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.HelloWorld");
- assertEquals("Hello Android World!", "Hello Android World!\n", res);
-
- res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.ResourceDumper");
- assertTrue("Android Resource Dumper started",
- res.contains("Android Resource Dumper started"));
- assertTrue("This Resource contains some text.",
- res.contains("This Resource contains some text."));
- }
-
-
- /**
- * This test does quite the same as test_execCreatedJar, but includes a manifest.
- * Note however that the Dalvik JAR format does not require this manifest.
- * We just test whether the manifest is placed correctly within the JAR by
- * dumping its contents read as a simple text resource.
- * No! We can't do that so easily either, as there are other (parent) JARs
- * with a manifest inside, taken with precedence.
- * So we will reopen the JAR as a JarFile and check the manifest
- * with a top level end-to-end approach.
- */
- public void test_execCreatedJarWithManifest () throws IOException, InterruptedException {
- File jarFile = File.createTempFile("cts_dalvikExecTest_", ".jar");
- jarFile.deleteOnExit();
-
- // Create the manifest:
- Manifest manifest = new Manifest();
- Attributes attrs = manifest.getMainAttributes();
- attrs.put(Attributes.Name.MANIFEST_VERSION, "3.1415962");
- attrs.put(Attributes.Name.MAIN_CLASS, "dalvikExecTest.HelloWorld");
- attrs.put(Attributes.Name.CLASS_PATH, jarFile.getName());
-
- // Create a JAR output stream on the temp file using the manifest:
- JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(jarFile), manifest);
-
- // Define the entry for the classes.dex:
- jarOut.putNextEntry(new JarEntry("classes.dex"));
-
- // Fill in the classes.dex contents, i.e. the Dalvik executable code:
- // (See below for the detailed source code contents.)
- Streams.copy(Support_Resources.getResourceStream("cts_dalvikExecTest_classes.dex"), jarOut);
-
- // Now add a resource file:
- //
- jarOut.putNextEntry(new JarEntry("dalvikExecTest/myResource"));
- jarOut.write("This Resource contains some text.".getBytes());
-
- // Close the stream to the completed JAR file.
- jarOut.close();
-
- // The resulting JAR file contains the classes listed at the end of this text,
- // like the 'cts_dalvikExecTest.jar' as part of the resources, too.
-
- String res;
-
- res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.HelloWorld");
- assertEquals("Hello Android World!", "Hello Android World!\n", res);
-
- res = execDalvik(jarFile.getAbsolutePath(), "dalvikExecTest.ResourceDumper");
- assertTrue("Android Resource Dumper started",
- res.contains("Android Resource Dumper started"));
- assertTrue("This Resource contains some text.",
- res.contains("This Resource contains some text."));
-
- // And now reread the manifest:
- //
- JarFile jarIn = new JarFile(jarFile);
- manifest = jarIn.getManifest();
- attrs = manifest.getMainAttributes();
- assertEquals("MANIFEST_VERSION must match!", "3.1415962",
- attrs.get(Attributes.Name.MANIFEST_VERSION));
- assertEquals("MAIN_CLASS must match!", "dalvikExecTest.HelloWorld",
- attrs.get(Attributes.Name.MAIN_CLASS));
- assertEquals("CLASS_PATH must match!", jarFile.getName(),
- attrs.get(Attributes.Name.CLASS_PATH));
- }
-
-
- /*
- * The following two classes are added, here, only for completeness.
- * They form the contents of the dalvikExecTest package contained
- * in the 'cts_dalvikExecTest_classes.dex' resource file.
- */
- /**
- * @hide
- */
- public static class HelloWorld {
-
- public static void main(String[] args) {
- System.out.println("Hello Android World!");
- }
-
- }
-
- public static class ResourceDumper {
-
- static ByteArrayOutputStream outputFrom (InputStream input) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buffer = new byte[512];
- int total = 0;
- int count;
- count = input.read(buffer);
- while (count != -1) {
- out.write(buffer, 0, count);
- total = total + count;
- count = input.read(buffer);
- }
- return out;
- }
-
- public static void main(String[] args) throws IOException {
- System.out.print("Android Resource Dumper started ");
- String fileName;
- if (args.length >= 1) {
- fileName = args[0];
- System.out.format("for argument '%s'.\n", fileName);
- } else {
- System.out.print("standard ");
- fileName = "myResource";
- System.out.println("for standard 'myResource'.");
- }
- InputStream is = ResourceDumper.class.getResourceAsStream(fileName);
- if (is != null) {
- System.out.println("Resource obtained and being dumped:");
- System.out.println(outputFrom(is).toString());
- }
- }
-
- }
-
-}
diff --git a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
index b2e012e..494520a 100644
--- a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
@@ -19,12 +19,18 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Random;
import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
import junit.framework.TestCase;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
public final class GZIPInputStreamTest extends TestCase {
@@ -112,6 +118,49 @@
}
}
+ // https://code.google.com/p/android/issues/detail?id=66409
+ public void testMultipleMembersWithCustomBufferSize() throws Exception {
+ final int[] memberSizes = new int[] { 1000, 2000 };
+
+ // We don't care what the exact contents of this file is, as long
+ // as the file has multiple members, and that the (compressed) size of
+ // the second member is larger than the size of the input buffer.
+ //
+ // There's no way to achieve this for a GZIPOutputStream so we generate
+ // pseudo-random sequence of bytes and assert that they don't compress
+ // well.
+ final Random r = new Random(10);
+ byte[] bytes = new byte[3000];
+ r.nextBytes(bytes);
+
+ File f = File.createTempFile("GZIPInputStreamTest", ".gzip");
+ int offset = 0;
+ for (int size : memberSizes) {
+ GZIPOutputStream gzos = null;
+ try {
+ FileOutputStream fos = new FileOutputStream(f, true /* append */);
+ gzos = new GZIPOutputStream(fos, size + 1);
+ gzos.write(bytes, offset, size);
+ offset += size;
+ gzos.finish();
+ } finally {
+ IoUtils.closeQuietly(gzos);
+ }
+ }
+
+ assertTrue(f.length() > 2048);
+
+ FileInputStream fis = new FileInputStream(f);
+ GZIPInputStream gzip = null;
+ try {
+ gzip = new GZIPInputStream(fis, memberSizes[0]);
+ byte[] unzipped = Streams.readFully(gzip);
+ assertTrue(Arrays.equals(bytes, unzipped));
+ } finally {
+ IoUtils.closeQuietly(gzip);
+ }
+ }
+
public static byte[] gunzip(byte[] bytes) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
InputStream in = new GZIPInputStream(bis);
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index 60af4d0..81ff673 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -21,11 +21,17 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
import java.util.Random;
+import java.util.Set;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
@@ -34,6 +40,8 @@
import java.util.zip.ZipOutputStream;
import junit.framework.TestCase;
+import tests.support.resource.Support_Resources;
+
public final class ZipFileTest extends TestCase {
/**
* Exercise Inflater's ability to refill the zlib's input buffer. As of this
@@ -160,7 +168,7 @@
}
assertEquals(expectedLength, count);
-
+ zip.close();
}
public void testInflatingStreamsRequiringZipRefill() throws IOException {
@@ -209,6 +217,96 @@
}
}
+ public void testNullCharset() throws IOException {
+ try {
+ new ZipFile(createTemporaryZipFile(), null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ // Tests that non-UTF8 encoded zip files can be interpreted. Relies on ZipOutputStream.
+ public void testNonUtf8Encoding() throws IOException {
+ Charset charset = Charset.forName("Cp437");
+ String encodingDependentString = "\u00FB";
+ assertEncodingDiffers(encodingDependentString, charset, StandardCharsets.US_ASCII,
+ StandardCharsets.UTF_8);
+ String name = "name" + encodingDependentString;
+ String comment = "comment" + encodingDependentString;
+
+ File result = createTemporaryZipFile();
+ OutputStream os = new BufferedOutputStream(new FileOutputStream(result));
+ ZipOutputStream out = new ZipOutputStream(os, charset);
+ out.setComment(comment);
+ ZipEntry writeEntry = new ZipEntry(name);
+ writeEntry.setComment(comment);
+ out.putNextEntry(writeEntry);
+ out.write("FileContentsIrrelevant".getBytes());
+ out.closeEntry();
+ out.close();
+
+ ZipFile zipFile = new ZipFile(result, StandardCharsets.US_ASCII);
+ assertNull(zipFile.getEntry(name));
+ assertFalse(zipFile.getComment().equals(comment));
+ zipFile.close();
+
+ zipFile = new ZipFile(result, charset);
+ ZipEntry readEntry = zipFile.getEntry(name);
+ assertNotNull(readEntry);
+ assertEquals(name, readEntry.getName());
+ assertEquals(comment, readEntry.getComment());
+ assertEquals(comment, zipFile.getComment());
+ zipFile.close();
+ }
+
+ // Tests that UTF8 encoded zip files can be interpreted when the constructor is provided with a
+ // non-UTF-8 encoding. Relies on ZipOutputStream.
+ public void testUtf8EncodingOverridesConstructor() throws IOException {
+ Charset charset = Charset.forName("Cp437");
+ String encodingDependentString = "\u00FB";
+ assertEncodingDiffers(encodingDependentString, charset, StandardCharsets.UTF_8);
+ String name = "name" + encodingDependentString;
+ String comment = "comment" + encodingDependentString;
+
+ File result = createTemporaryZipFile();
+ OutputStream os = new BufferedOutputStream(new FileOutputStream(result));
+ ZipOutputStream out = new ZipOutputStream(os, StandardCharsets.UTF_8);
+ // The file comment does not get meta-data about the character encoding.
+ out.setComment(comment);
+ // The entry will be tagged as being UTF-8 encoded.
+ ZipEntry writeEntry = new ZipEntry(name);
+ writeEntry.setComment(comment);
+ out.putNextEntry(writeEntry);
+ out.write("FileContentsIrrelevant".getBytes());
+ out.closeEntry();
+ out.close();
+
+ ZipFile zipFile = new ZipFile(result, charset);
+ // The entry should be found, because it should be tagged as being UTF-8 encoded.
+ ZipEntry readEntry = zipFile.getEntry(name);
+ assertNotNull(readEntry);
+ assertEquals(name, readEntry.getName());
+ assertEquals(comment, readEntry.getComment());
+ // We expect the comment to be mangled because it is not tagged.
+ assertFalse(zipFile.getComment().equals(comment));
+ zipFile.close();
+ }
+
+ /**
+ * Asserts the byte encoding for the string is different for all the supplied character
+ * sets.
+ */
+ private void assertEncodingDiffers(String string, Charset... charsets) {
+ Set<List<Byte>> encodings = new HashSet<List<Byte>>();
+ for (int i = 0; i < charsets.length; i++) {
+ List<Byte> byteList = new ArrayList<Byte>();
+ for (byte b : string.getBytes(charsets[i])) {
+ byteList.add(b);
+ }
+ assertTrue("Encoding has been seen before", encodings.add(byteList));
+ }
+ }
+
/**
* Compresses the given number of files, each of the given size, into a .zip archive.
*/
@@ -218,21 +316,23 @@
byte[] writeBuffer = new byte[8192];
Random random = new Random();
- ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(result)));
- for (int entry = 0; entry < entryCount; ++entry) {
- ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
- out.putNextEntry(ze);
+ ZipOutputStream out = createZipOutputStream(result);
+ try {
+ for (int entry = 0; entry < entryCount; ++entry) {
+ ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+ out.putNextEntry(ze);
- for (int i = 0; i < entrySize; i += writeBuffer.length) {
- random.nextBytes(writeBuffer);
- int byteCount = Math.min(writeBuffer.length, entrySize - i);
- out.write(writeBuffer, 0, byteCount);
+ for (int i = 0; i < entrySize; i += writeBuffer.length) {
+ random.nextBytes(writeBuffer);
+ int byteCount = Math.min(writeBuffer.length, entrySize - i);
+ out.write(writeBuffer, 0, byteCount);
+ }
+
+ out.closeEntry();
}
-
- out.closeEntry();
+ } finally {
+ out.close();
}
-
- out.close();
return result;
}
@@ -468,4 +568,30 @@
out.closeEntry();
out.close();
}
+
+ /**
+ * RI does not allow reading of an empty zip using a {@link ZipFile}.
+ */
+ public void testConstructorFailsWhenReadingEmptyZipArchive() throws IOException {
+
+ File resources = Support_Resources.createTempFolder();
+ File emptyZip = Support_Resources.copyFile(
+ resources, "java/util/zip", "EmptyArchive.zip");
+
+ try {
+ // The following should fail with an exception but if it doesn't then we need to clean
+ // up the resource so we need a reference to it.
+ ZipFile zipFile = new ZipFile(emptyZip);
+
+ // Clean up the resource.
+ try {
+ zipFile.close();
+ } catch (Exception e) {
+ // Ignore
+ }
+ fail();
+ } catch (ZipException expected) {
+ // expected
+ }
+ }
}
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 cb98322..2daa127 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
@@ -20,15 +20,24 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
import java.util.Random;
+import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
+
import junit.framework.TestCase;
+import tests.support.resource.Support_Resources;
+
public final class ZipInputStreamTest extends TestCase {
+
public void testShortMessage() throws IOException {
byte[] data = "Hello World".getBytes("UTF-8");
byte[] zipped = ZipOutputStreamTest.zip("short", data);
@@ -41,6 +50,97 @@
assertTrue(Arrays.equals(data, unzip("r", ZipOutputStreamTest.zip("r", data))));
}
+ public void testNullCharset() throws IOException {
+ try {
+ new ZipInputStream(new ByteArrayInputStream(new byte[1]), null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ // Tests that non-UTF8 encoded zip file entries can be interpreted. Relies on ZipOutputStream.
+ public void testNonUtf8Encoding() throws IOException {
+ Charset charset = Charset.forName("Cp437");
+ String encodingDependentString = "\u00FB";
+ assertEncodingDiffers(encodingDependentString, charset, StandardCharsets.US_ASCII,
+ StandardCharsets.UTF_8);
+ String name = "name" + encodingDependentString;
+ String comment = "comment" + encodingDependentString;
+
+ ByteArrayOutputStream bytesOutputStream = new ByteArrayOutputStream();
+ ZipOutputStream out = new ZipOutputStream(bytesOutputStream, charset);
+ ZipEntry writeEntry = new ZipEntry(name);
+ writeEntry.setComment(comment);
+ out.putNextEntry(writeEntry);
+ out.write("FileContentsIrrelevant".getBytes());
+ out.closeEntry();
+ out.close();
+
+ ByteArrayInputStream bytesInputStream =
+ new ByteArrayInputStream(bytesOutputStream.toByteArray());
+ ZipInputStream in = new ZipInputStream(bytesInputStream, StandardCharsets.US_ASCII);
+ ZipEntry readEntry = in.getNextEntry();
+ // Due to the way ZipInputStream works it never returns entry comments.
+ assertNull("ZipInputStream must not retrieve comments", readEntry.getComment());
+ assertFalse(readEntry.getName().equals(name));
+ in.close();
+
+ bytesInputStream = new ByteArrayInputStream(bytesOutputStream.toByteArray());
+ in = new ZipInputStream(bytesInputStream, charset);
+ readEntry = in.getNextEntry();
+ // Because ZipInputStream never reads the central directory it never returns entry
+ // comments or the file comment.
+ assertNull("ZipInputStream must not retrieve comments", readEntry.getComment());
+ assertEquals(name, readEntry.getName());
+ in.close();
+ }
+
+ // Tests that UTF8 encoded zip file entries can be interpreted when the constructor is provided
+ // with a non-UTF-8 encoding. Relies on ZipOutputStream.
+ public void testUtf8EncodingOverridesConstructor() throws IOException {
+ Charset charset = Charset.forName("Cp437");
+ String encodingDependentString = "\u00FB";
+ assertEncodingDiffers(encodingDependentString, charset, StandardCharsets.UTF_8);
+ String name = "name" + encodingDependentString;
+ String comment = "comment" + encodingDependentString;
+
+ ByteArrayOutputStream bytesOutputStream = new ByteArrayOutputStream();
+ ZipOutputStream out = new ZipOutputStream(bytesOutputStream, StandardCharsets.UTF_8);
+ // The entry will be tagged as being UTF-8 encoded.
+ ZipEntry writeEntry = new ZipEntry(name);
+ writeEntry.setComment(comment);
+ out.putNextEntry(writeEntry);
+ out.write("FileContentsIrrelevant".getBytes());
+ out.closeEntry();
+ out.close();
+
+ ByteArrayInputStream bytesInputStream =
+ new ByteArrayInputStream(bytesOutputStream.toByteArray());
+ ZipInputStream in = new ZipInputStream(bytesInputStream, charset);
+ ZipEntry readEntry = in.getNextEntry();
+ // Because ZipInputStream never reads the central directory it never returns entry
+ // comments or the file comment.
+ assertNull("ZipInputStream must not retrieve comments", readEntry.getComment());
+ assertNotNull(readEntry);
+ assertEquals(name, readEntry.getName());
+ in.close();
+ }
+
+ /**
+ * Asserts the byte encoding for the string is different for all the supplied character
+ * sets.
+ */
+ private void assertEncodingDiffers(String string, Charset... charsets) {
+ Set<List<Byte>> encodings = new HashSet<List<Byte>>();
+ for (int i = 0; i < charsets.length; i++) {
+ List<Byte> byteList = new ArrayList<Byte>();
+ for (byte b : string.getBytes(charsets[i])) {
+ byteList.add(b);
+ }
+ assertTrue("Encoding has been seen before", encodings.add(byteList));
+ }
+ }
+
public static byte[] unzip(String name, byte[] bytes) throws IOException {
ZipInputStream in = new ZipInputStream(new ByteArrayInputStream(bytes));
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -59,4 +159,18 @@
in.close();
return out.toByteArray();
}
+
+ /**
+ * Reference implementation allows reading of empty zip using a {@link ZipInputStream}.
+ */
+ public void testReadEmpty() throws IOException {
+ InputStream emptyZipIn = Support_Resources.getStream("java/util/zip/EmptyArchive.zip");
+ ZipInputStream in = new ZipInputStream(emptyZipIn);
+ try {
+ ZipEntry entry = in.getNextEntry();
+ assertNull("An empty zip has no entries", entry);
+ } finally {
+ in.close();
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
index e7c518f..92afffa 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
@@ -16,15 +16,16 @@
package libcore.java.util.zip;
-import java.io.ByteArrayInputStream;
+import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;
import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
+import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;
import junit.framework.TestCase;
@@ -60,4 +61,88 @@
zippedOut.close();
return bytesOut.toByteArray();
}
+
+ /**
+ * Reference implementation does NOT allow writing of an empty zip using a
+ * {@link ZipOutputStream}.
+ */
+ public void testCreateEmpty() throws IOException {
+ File result = File.createTempFile("ZipFileTest", "zip");
+ ZipOutputStream out =
+ new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(result)));
+ try {
+ out.close();
+ fail("Close on empty stream failed to throw exception");
+ } catch (ZipException e) {
+ // expected
+ }
+ }
+
+ public void testNullCharset() throws IOException {
+ try {
+ new ZipOutputStream(new ByteArrayOutputStream(), null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ /** Regression test for null comment causing a NullPointerException during write. */
+ public void testNullComment() throws IOException {
+ ZipOutputStream out = new ZipOutputStream(new ByteArrayOutputStream());
+ out.setComment(null);
+ out.putNextEntry(new ZipEntry("name"));
+ out.write(new byte[1]);
+ out.closeEntry();
+ out.finish();
+ }
+
+ /** Contrived test to force a longer name than can be stored. */
+ public void testLongName() throws IOException {
+ int maxNameBytes = 0xffff; // 2 bytes
+ String longName = createString(maxNameBytes);
+
+ ZipEntry entry = new ZipEntry(longName);
+
+ // Using UTF-16 will result in name bytes twice as large as is supported by Zip.
+ // UTF-16 is an unlikely character set to actually want to use with Zip but enables
+ // the edge-case behavior required without using direct field access.
+ ZipOutputStream out = new ZipOutputStream(
+ new ByteArrayOutputStream(), StandardCharsets.UTF_16);
+ try {
+ out.putNextEntry(entry);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ /** Contrived test to force a longer comment than can be stored. */
+ public void testLongComment() throws IOException {
+ int maxCommentBytes = 0xffff; // 2 bytes
+ String longComment = createString(maxCommentBytes);
+
+ ZipEntry entry = new ZipEntry("name");
+ // setComment() should pass, because it is at the limit of what ZipEntry will detect as
+ // valid (since it uses UTF-8 as a worst-case guess).
+ entry.setComment(longComment);
+
+ // Using UTF-16 will result in comment bytes twice as large as is supported by Zip.
+ // UTF-16 is an unlikely character set to actually want to use with Zip but enables
+ // the edge-case behavior required without using direct field access.
+ ZipOutputStream out = new ZipOutputStream(
+ new ByteArrayOutputStream(), StandardCharsets.UTF_16);
+ try {
+ out.putNextEntry(entry);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private static String createString(int numChars) {
+ char c = 'a';
+ StringBuilder sb = new StringBuilder(numChars);
+ for (int i = 0; i < numChars; i++) {
+ sb.append(c);
+ }
+ return sb.toString();
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index d32a838..f7a0a72 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -26,6 +26,7 @@
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
@@ -97,12 +98,16 @@
return false;
}
}
- // stream modes CFB, CTR, CTS, OFB with PKCS5Padding don't really make sense
+ // stream modes CFB, CTR, CTS, OFB with PKCS5Padding or PKCS7Padding don't really make sense
if (!provider.equals("AndroidOpenSSL") &&
(algorithm.equals("AES/CFB/PKCS5PADDING")
+ || algorithm.equals("AES/CFB/PKCS7PADDING")
|| algorithm.equals("AES/CTR/PKCS5PADDING")
+ || algorithm.equals("AES/CTR/PKCS7PADDING")
|| algorithm.equals("AES/CTS/PKCS5PADDING")
- || algorithm.equals("AES/OFB/PKCS5PADDING"))) {
+ || algorithm.equals("AES/CTS/PKCS7PADDING")
+ || algorithm.equals("AES/OFB/PKCS5PADDING")
+ || algorithm.equals("AES/OFB/PKCS7PADDING"))) {
return false;
}
return true;
@@ -276,16 +281,22 @@
static {
setExpectedBlockSize("AES", 16);
setExpectedBlockSize("AES/CBC/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/CBC/PKCS7PADDING", 16);
setExpectedBlockSize("AES/CBC/NOPADDING", 16);
setExpectedBlockSize("AES/CFB/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/CFB/PKCS7PADDING", 16);
setExpectedBlockSize("AES/CFB/NOPADDING", 16);
setExpectedBlockSize("AES/CTR/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/CTR/PKCS7PADDING", 16);
setExpectedBlockSize("AES/CTR/NOPADDING", 16);
setExpectedBlockSize("AES/CTS/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/CTS/PKCS7PADDING", 16);
setExpectedBlockSize("AES/CTS/NOPADDING", 16);
setExpectedBlockSize("AES/ECB/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/ECB/PKCS7PADDING", 16);
setExpectedBlockSize("AES/ECB/NOPADDING", 16);
setExpectedBlockSize("AES/OFB/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/OFB/PKCS7PADDING", 16);
setExpectedBlockSize("AES/OFB/NOPADDING", 16);
setExpectedBlockSize("GCM", 16);
setExpectedBlockSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
@@ -317,16 +328,22 @@
setExpectedBlockSize("DESEDE", 8);
setExpectedBlockSize("DESEDE/CBC/PKCS5PADDING", 8);
+ setExpectedBlockSize("DESEDE/CBC/PKCS7PADDING", 8);
setExpectedBlockSize("DESEDE/CBC/NOPADDING", 8);
setExpectedBlockSize("DESEDE/CFB/PKCS5PADDING", 8);
+ setExpectedBlockSize("DESEDE/CFB/PKCS7PADDING", 8);
setExpectedBlockSize("DESEDE/CFB/NOPADDING", 8);
setExpectedBlockSize("DESEDE/CTR/PKCS5PADDING", 8);
+ setExpectedBlockSize("DESEDE/CTR/PKCS7PADDING", 8);
setExpectedBlockSize("DESEDE/CTR/NOPADDING", 8);
setExpectedBlockSize("DESEDE/CTS/PKCS5PADDING", 8);
+ setExpectedBlockSize("DESEDE/CTS/PKCS7PADDING", 8);
setExpectedBlockSize("DESEDE/CTS/NOPADDING", 8);
setExpectedBlockSize("DESEDE/ECB/PKCS5PADDING", 8);
+ setExpectedBlockSize("DESEDE/ECB/PKCS7PADDING", 8);
setExpectedBlockSize("DESEDE/ECB/NOPADDING", 8);
setExpectedBlockSize("DESEDE/OFB/PKCS5PADDING", 8);
+ setExpectedBlockSize("DESEDE/OFB/PKCS7PADDING", 8);
setExpectedBlockSize("DESEDE/OFB/NOPADDING", 8);
setExpectedBlockSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", 8);
setExpectedBlockSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", 8);
@@ -427,11 +444,17 @@
setExpectedOutputSize("AES", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/CFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/CTR/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/CTS/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("GCM", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
@@ -444,17 +467,27 @@
setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", 16);
// AndroidOpenSSL returns zero for the non-block ciphers
setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+ setExpectedOutputSize("AES/CFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+ setExpectedOutputSize("AES/CTR/PKCS7PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+ setExpectedOutputSize("AES/CTS/PKCS7PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+ setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
setExpectedOutputSize("AES", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/CFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/CTR/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/CTS/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("GCM", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
@@ -467,9 +500,13 @@
setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
// AndroidOpenSSL returns the block size for the block ciphers
setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
+ setExpectedOutputSize("AES/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
+ setExpectedOutputSize("AES/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
setExpectedOutputSize("DESEDE/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 8);
+ setExpectedOutputSize("DESEDE/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 8);
setExpectedOutputSize("DESEDE/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 8);
+ setExpectedOutputSize("DESEDE/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 8);
if (StandardNames.IS_RI) {
setExpectedOutputSize("AESWRAP", Cipher.WRAP_MODE, 8);
@@ -503,11 +540,17 @@
setExpectedOutputSize("DESEDE", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("DESEDE/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+ setExpectedOutputSize("DESEDE/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("DESEDE/CFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+ setExpectedOutputSize("DESEDE/CFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("DESEDE/CTR/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+ setExpectedOutputSize("DESEDE/CTR/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("DESEDE/CTS/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+ setExpectedOutputSize("DESEDE/CTS/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("DESEDE/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+ setExpectedOutputSize("DESEDE/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("DESEDE/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+ setExpectedOutputSize("DESEDE/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", Cipher.ENCRYPT_MODE, 8);
setExpectedOutputSize("PBEWITHMD5ANDTRIPLEDES", Cipher.ENCRYPT_MODE, 8);
@@ -515,11 +558,17 @@
setExpectedOutputSize("DESEDE", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("DESEDE/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("DESEDE/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("DESEDE/CFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("DESEDE/CFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("DESEDE/CTR/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("DESEDE/CTR/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("DESEDE/CTS/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("DESEDE/CTS/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("DESEDE/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("DESEDE/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("DESEDE/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("DESEDE/OFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5ANDTRIPLEDES", Cipher.DECRYPT_MODE, 0);
@@ -708,6 +757,7 @@
}
if (algorithm.equals("AES/CBC/NOPADDING")
|| algorithm.equals("AES/CBC/PKCS5PADDING")
+ || algorithm.equals("AES/CBC/PKCS7PADDING")
|| algorithm.equals("AES/CFB/NOPADDING")
|| algorithm.equals("AES/CTR/NOPADDING")
|| algorithm.equals("AES/CTS/NOPADDING")
@@ -718,6 +768,7 @@
}
if (algorithm.equals("DESEDE/CBC/NOPADDING")
|| algorithm.equals("DESEDE/CBC/PKCS5PADDING")
+ || algorithm.equals("DESEDE/CBC/PKCS7PADDING")
|| algorithm.equals("DESEDE/CFB/NOPADDING")
|| algorithm.equals("DESEDE/CTR/NOPADDING")
|| algorithm.equals("DESEDE/CTS/NOPADDING")
@@ -782,11 +833,70 @@
public abstract void setup();
}
+ public void testCipher_getInstance_SuppliedProviderNotRegistered_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ {
+ Cipher c = Cipher.getInstance("FOO", mockProvider);
+ c.init(Cipher.ENCRYPT_MODE, new MockKey());
+ assertEquals(mockProvider, c.getProvider());
+ }
+ }
+
+ public void testCipher_getInstance_SuppliedProviderNotRegistered_MultipartTransform_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ {
+ Cipher c = Cipher.getInstance("FOO/FOO/FOO", mockProvider);
+ c.init(Cipher.ENCRYPT_MODE, new MockKey());
+ assertEquals(mockProvider, c.getProvider());
+ }
+ }
+
+ public void testCipher_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ {
+ Provider mockProvider2 = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ }
+ };
+ Cipher c = Cipher.getInstance("FOO", mockProvider2);
+ assertEquals(mockProvider2, c.getProvider());
+ }
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
public void testCipher_getInstance_DelayedInitialization_KeyType() throws Exception {
Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.SpecificKeyTypes.class.getName());
- put("Cipher.FOO SupportedKeyClasses", this.getClass().getPackage().getName() + ".MockKey");
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+ Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.SpecificKeyTypes2.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey2.class.getName());
}
};
Provider mockProviderAll = new MockProvider("MockProviderAll") {
@@ -796,13 +906,27 @@
};
Security.addProvider(mockProviderSpecific);
+ Security.addProvider(mockProviderSpecific2);
Security.addProvider(mockProviderAll);
try {
{
+ System.out.println(Arrays.deepToString(Security.getProviders("Cipher.FOO")));
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.ENCRYPT_MODE, new MockKey());
assertEquals(mockProviderSpecific, c.getProvider());
+
+ try {
+ c.init(Cipher.ENCRYPT_MODE, new MockKey2());
+ assertEquals(mockProviderSpecific2, c.getProvider());
+ if (StandardNames.IS_RI) {
+ fail("RI was broken before; fix tests now that it works!");
+ }
+ } catch (InvalidKeyException e) {
+ if (!StandardNames.IS_RI) {
+ fail("Non-RI should select the right provider");
+ }
+ }
}
{
@@ -832,10 +956,28 @@
}
} finally {
Security.removeProvider(mockProviderSpecific.getName());
+ Security.removeProvider(mockProviderSpecific2.getName());
Security.removeProvider(mockProviderAll.getName());
}
}
+ public void testCipher_getInstance_WrongType_Failure() throws Exception {
+ Provider mockProviderInvalid = new MockProvider("MockProviderInvalid") {
+ public void setup() {
+ put("Cipher.FOO", Object.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderInvalid);
+ try {
+ Cipher.getInstance("FOO");
+ fail("Should not find any matching providers");
+ } catch (NoSuchAlgorithmException expected) {
+ } finally {
+ Security.removeProvider(mockProviderInvalid.getName());
+ }
+ }
+
public void test_getInstance() throws Exception {
final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
PrintStream out = new PrintStream(errBuffer);
@@ -986,7 +1128,8 @@
getExpectedBlockSize(algorithm, encryptMode, providerName), c.getBlockSize());
assertEquals(cipherID + " getOutputSize(0) encryptMode",
getExpectedOutputSize(algorithm, encryptMode, providerName), c.getOutputSize(0));
- if (algorithm.endsWith("/PKCS5PADDING") && isStreamMode(algorithm)) {
+ if ((algorithm.endsWith("/PKCS5PADDING") || algorithm.endsWith("/PKCS7PADDING"))
+ && isStreamMode(algorithm)) {
assertEquals(getExpectedOutputSize(algorithm, encryptMode, providerName),
c.doFinal(new byte[1]).length);
}
@@ -2334,7 +2477,7 @@
};
private static class CipherTestParam {
- public final String mode;
+ public final String transformation;
public final byte[] key;
@@ -2346,9 +2489,9 @@
public final byte[] plaintextPadded;
- public CipherTestParam(String mode, byte[] key, byte[] iv, byte[] plaintext,
+ public CipherTestParam(String transformation, byte[] key, byte[] iv, byte[] plaintext,
byte[] plaintextPadded, byte[] ciphertext) {
- this.mode = mode;
+ this.transformation = transformation;
this.key = key;
this.iv = iv;
this.plaintext = plaintext;
@@ -2359,13 +2502,24 @@
private static List<CipherTestParam> CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
static {
- CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB", AES_128_KEY,
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS5Padding", AES_128_KEY,
+ null,
+ AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
+ AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
+ AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
+ // PKCS#5 is assumed to be equivalent to PKCS#7 -- same test vectors are thus used for both.
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS7Padding", AES_128_KEY,
null,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
if (IS_UNLIMITED) {
- CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC", AES_256_KEY,
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS5Padding", AES_256_KEY,
+ AES_256_CBC_PKCS5Padding_TestVector_1_IV,
+ AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
+ AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded,
+ AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext));
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS7Padding", AES_256_KEY,
AES_256_CBC_PKCS5Padding_TestVector_1_IV,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded,
@@ -2386,7 +2540,7 @@
try {
checkCipher(p, provider);
} catch (Exception e) {
- out.append("Error encountered checking " + p.mode + ", keySize="
+ out.append("Error encountered checking " + p.transformation + ", keySize="
+ (p.key.length * 8)
+ " with provider " + provider + "\n");
@@ -2401,7 +2555,7 @@
private void checkCipher(CipherTestParam p, String provider) throws Exception {
SecretKey key = new SecretKeySpec(p.key, "AES");
- Cipher c = Cipher.getInstance(p.mode + "/PKCS5Padding", provider);
+ Cipher c = Cipher.getInstance(p.transformation, provider);
AlgorithmParameterSpec spec = null;
if (p.iv != null) {
spec = new IvParameterSpec(p.iv);
@@ -2486,7 +2640,8 @@
Arrays.toString(Arrays.copyOfRange(actualPlaintext, 1, p.plaintext.length + 1)));
}
- Cipher cNoPad = Cipher.getInstance(p.mode + "/NoPadding", provider);
+ Cipher cNoPad = Cipher.getInstance(
+ getCipherTransformationWithNoPadding(p.transformation), provider);
cNoPad.init(Cipher.DECRYPT_MODE, key, spec);
final byte[] actualPlaintextPadded = cNoPad.doFinal(p.ciphertext);
@@ -2516,6 +2671,23 @@
}
}
+ /**
+ * Gets the Cipher transformation with the same algorithm and mode as the provided one but
+ * which uses no padding.
+ */
+ private static String getCipherTransformationWithNoPadding(String transformation) {
+ // The transformation is assumed to be in the Algorithm/Mode/Padding format.
+ int paddingModeDelimiterIndex = transformation.lastIndexOf('/');
+ if (paddingModeDelimiterIndex == -1) {
+ fail("No padding mode delimiter: " + transformation);
+ }
+ String paddingMode = transformation.substring(paddingModeDelimiterIndex + 1);
+ if (!paddingMode.toLowerCase().endsWith("padding")) {
+ fail("No padding mode specified:" + transformation);
+ }
+ return transformation.substring(0, paddingModeDelimiterIndex) + "/NoPadding";
+ }
+
public void testCipher_updateAAD_BeforeInit_Failure() throws Exception {
Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
@@ -2605,7 +2777,7 @@
try {
checkCipher_ShortBlock_Failure(p, provider);
} catch (Exception e) {
- out.append("Error encountered checking " + p.mode + ", keySize="
+ out.append("Error encountered checking " + p.transformation + ", keySize="
+ (p.key.length * 8)
+ " with provider " + provider + "\n");
e.printStackTrace(out);
@@ -2619,7 +2791,8 @@
private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) throws Exception {
SecretKey key = new SecretKeySpec(p.key, "AES");
- Cipher c = Cipher.getInstance(p.mode + "/NoPadding", provider);
+ Cipher c = Cipher.getInstance(
+ getCipherTransformationWithNoPadding(p.transformation), provider);
if (c.getBlockSize() == 0) {
return;
}
diff --git a/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java b/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
index b09d883..6742cf3 100644
--- a/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
+++ b/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
@@ -38,7 +38,17 @@
@Override
public void checkKeyType(Key key) throws InvalidKeyException {
if (!(key instanceof MockKey)) {
- throw new InvalidKeyException("Must be a mock key!");
+ throw new InvalidKeyException("Must be MockKey!");
+ }
+ }
+ }
+
+ public static class SpecificKeyTypes2 extends MockCipherSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ System.err.println("Checking key of type " + key.getClass().getName());
+ if (!(key instanceof MockKey2)) {
+ throw new InvalidKeyException("Must be MockKey2!");
}
}
}
@@ -51,12 +61,16 @@
@Override
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
- throw new UnsupportedOperationException("not implemented");
+ if (!"FOO".equals(mode)) {
+ throw new UnsupportedOperationException("not implemented");
+ }
}
@Override
protected void engineSetPadding(String padding) throws NoSuchPaddingException {
- throw new UnsupportedOperationException("not implemented");
+ if (!"FOO".equals(padding)) {
+ throw new UnsupportedOperationException("not implemented");
+ }
}
@Override
diff --git a/luni/src/test/java/libcore/javax/crypto/MockKey2.java b/luni/src/test/java/libcore/javax/crypto/MockKey2.java
new file mode 100644
index 0000000..44ac0cc
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MockKey2.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 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 libcore.javax.crypto;
+
+import java.security.Key;
+
+/**
+ * A mock Key class used for testing.
+ */
+@SuppressWarnings("serial")
+public class MockKey2 implements Key {
+ @Override
+ public String getAlgorithm() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public String getFormat() {
+ return "MOCK";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
index 54f899a..0e13fb0 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
@@ -16,17 +16,31 @@
package libcore.javax.net.ssl;
+import java.security.InvalidAlgorithmParameterException;
import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
import java.security.Provider;
+import java.security.Security;
+import java.security.UnrecoverableKeyException;
+import java.util.concurrent.Callable;
import libcore.java.security.StandardNames;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.KeyManagerFactorySpi;
+import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.TrustManagerFactorySpi;
+import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
public class SSLContextTest extends TestCase {
@@ -121,16 +135,183 @@
assertEquals(StandardNames.JSSE_PROVIDER_NAME, provider.getName());
}
- public void test_SSLContext_init() throws Exception {
+ public void test_SSLContext_init_Default() throws Exception {
+ // Assert that initializing a default SSLContext fails because it's supposed to be
+ // initialized already.
+ SSLContext sslContext = SSLContext.getInstance(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT);
+ try {
+ sslContext.init(null, null, null);
+ fail();
+ } catch (KeyManagementException expected) {}
+ try {
+ sslContext.init(new KeyManager[0], new TrustManager[0], null);
+ fail();
+ } catch (KeyManagementException expected) {}
+ try {
+ sslContext.init(
+ new KeyManager[] {new KeyManager() {}},
+ new TrustManager[] {new TrustManager() {}},
+ null);
+ fail();
+ } catch (KeyManagementException expected) {}
+ }
+
+ public void test_SSLContext_init_withNullManagerArrays() throws Exception {
+ // Assert that SSLContext.init works fine even when provided with null arrays of
+ // KeyManagers and TrustManagers.
+ // The contract of SSLContext.init is that it will for default X.509 KeyManager and
+ // TrustManager from the highest priority KeyManagerFactory and TrustManagerFactory.
for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
- SSLContext sslContext = SSLContext.getInstance(protocol);
if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
- try {
- sslContext.init(null, null, null);
- } catch (KeyManagementException expected) {
+ // Default SSLContext is provided in an already initialized state
+ continue;
+ }
+ SSLContext sslContext = SSLContext.getInstance(protocol);
+ sslContext.init(null, null, null);
+ }
+ }
+
+ public void test_SSLContext_init_withEmptyManagerArrays() throws Exception {
+ // Assert that SSLContext.init works fine even when provided with empty arrays of
+ // KeyManagers and TrustManagers.
+ // The contract of SSLContext.init is that it will not look for default X.509 KeyManager and
+ // TrustManager.
+ // This test thus installs a Provider of KeyManagerFactory and TrustManagerFactory whose
+ // factories throw exceptions which will make this test fail if the factories are used.
+ Provider provider = new ThrowExceptionKeyAndTrustManagerFactoryProvider();
+ invokeWithHighestPrioritySecurityProvider(provider, new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ assertEquals(
+ ThrowExceptionKeyAndTrustManagerFactoryProvider.class,
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
+ .getProvider().getClass());
+ assertEquals(
+ ThrowExceptionKeyAndTrustManagerFactoryProvider.class,
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
+ .getProvider().getClass());
+
+ KeyManager[] keyManagers = new KeyManager[0];
+ TrustManager[] trustManagers = new TrustManager[0];
+ for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+ if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+ // Default SSLContext is provided in an already initialized state
+ continue;
+ }
+ SSLContext sslContext = SSLContext.getInstance(protocol);
+ sslContext.init(keyManagers, trustManagers, null);
}
- } else {
- sslContext.init(null, null, null);
+
+ return null;
+ }
+ });
+ }
+
+ public void test_SSLContext_init_withoutX509() throws Exception {
+ // Assert that SSLContext.init works fine even when provided with KeyManagers and
+ // TrustManagers which don't include the X.509 ones.
+ // The contract of SSLContext.init is that it will not look for default X.509 KeyManager and
+ // TrustManager.
+ // This test thus installs a Provider of KeyManagerFactory and TrustManagerFactory whose
+ // factories throw exceptions which will make this test fail if the factories are used.
+ Provider provider = new ThrowExceptionKeyAndTrustManagerFactoryProvider();
+ invokeWithHighestPrioritySecurityProvider(provider, new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ assertEquals(
+ ThrowExceptionKeyAndTrustManagerFactoryProvider.class,
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
+ .getProvider().getClass());
+ assertEquals(
+ ThrowExceptionKeyAndTrustManagerFactoryProvider.class,
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
+ .getProvider().getClass());
+
+ KeyManager[] keyManagers = new KeyManager[] {new KeyManager() {}};
+ TrustManager[] trustManagers = new TrustManager[] {new TrustManager() {}};
+ for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+ if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+ // Default SSLContext is provided in an already initialized state
+ continue;
+ }
+ SSLContext sslContext = SSLContext.getInstance(protocol);
+ sslContext.init(keyManagers, trustManagers, null);
+ }
+
+ return null;
+ }
+ });
+ }
+
+ public static class ThrowExceptionKeyAndTrustManagerFactoryProvider extends Provider {
+ public ThrowExceptionKeyAndTrustManagerFactoryProvider() {
+ super("ThrowExceptionKeyAndTrustManagerProvider",
+ 1.0,
+ "SSLContextTest fake KeyManagerFactory and TrustManagerFactory provider");
+
+ put("TrustManagerFactory." + TrustManagerFactory.getDefaultAlgorithm(),
+ ThrowExceptionTrustManagagerFactorySpi.class.getName());
+ put("TrustManagerFactory.PKIX", ThrowExceptionTrustManagagerFactorySpi.class.getName());
+
+ put("KeyManagerFactory." + KeyManagerFactory.getDefaultAlgorithm(),
+ ThrowExceptionKeyManagagerFactorySpi.class.getName());
+ put("KeyManagerFactory.PKIX", ThrowExceptionKeyManagagerFactorySpi.class.getName());
+ }
+ }
+
+ public static class ThrowExceptionTrustManagagerFactorySpi extends TrustManagerFactorySpi {
+ @Override
+ protected void engineInit(KeyStore ks) throws KeyStoreException {
+ fail();
+ }
+
+ @Override
+ protected void engineInit(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException {
+ fail();
+ }
+
+ @Override
+ protected TrustManager[] engineGetTrustManagers() {
+ throw new AssertionFailedError();
+ }
+ }
+
+ public static class ThrowExceptionKeyManagagerFactorySpi extends KeyManagerFactorySpi {
+ @Override
+ protected void engineInit(KeyStore ks, char[] password) throws KeyStoreException,
+ NoSuchAlgorithmException, UnrecoverableKeyException {
+ fail();
+ }
+
+ @Override
+ protected void engineInit(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException {
+ fail();
+ }
+
+ @Override
+ protected KeyManager[] engineGetKeyManagers() {
+ throw new AssertionFailedError();
+ }
+ }
+
+ /**
+ * Installs the specified security provider as the highest provider, invokes the provided
+ * {@link Callable}, and removes the provider.
+ *
+ * @return result returned by the {@code callable}.
+ */
+ private static <T> T invokeWithHighestPrioritySecurityProvider(
+ Provider provider, Callable<T> callable) throws Exception {
+ int providerPosition = -1;
+ try {
+ providerPosition = Security.insertProviderAt(provider, 1);
+ assertEquals(1, providerPosition);
+ return callable.call();
+ } finally {
+ if (providerPosition != -1) {
+ Security.removeProvider(provider.getName());
}
}
}
@@ -263,8 +444,16 @@
assertNull(testContext.clientStorePassword);
assertNotNull(testContext.serverKeyStore);
assertEquals(StandardNames.IS_RI, testContext.serverStorePassword != null);
- assertNotNull(testContext.clientKeyManager);
- assertNotNull(testContext.serverKeyManager);
+ assertNotNull(testContext.clientKeyManagers);
+ assertNotNull(testContext.serverKeyManagers);
+ if (testContext.clientKeyManagers.length == 0) {
+ fail("No client KeyManagers");
+ }
+ if (testContext.serverKeyManagers.length == 0) {
+ fail("No server KeyManagers");
+ }
+ assertNotNull(testContext.clientKeyManagers[0]);
+ assertNotNull(testContext.serverKeyManagers[0]);
assertNotNull(testContext.clientTrustManager);
assertNotNull(testContext.serverTrustManager);
assertNotNull(testContext.clientContext);
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index bf332c1..7adbdf5 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -28,6 +28,7 @@
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509ExtendedKeyManager;
import junit.framework.TestCase;
import libcore.java.security.StandardNames;
import libcore.java.security.TestKeyStore;
@@ -81,9 +82,7 @@
.ca(true)
.build();
test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, false);
- if (StandardNames.IS_RI) {
- test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, true);
- }
+ test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, true);
}
private void test_SSLEngine_getSupportedCipherSuites_connect(TestKeyStore testKeyStore,
boolean secureRenegotiation)
@@ -93,96 +92,135 @@
// Create a TestSSLContext where the KeyManager returns wrong (randomly generated) private
// keys, matching the algorithm and parameters of the correct keys.
// I couldn't find a more elegant way to achieve this other than temporarily replacing the
- // first element of TestKeyStore.keyManagers while invoking TestSSLContext.create.
+ // first X509ExtendedKeyManager element of TestKeyStore.keyManagers while invoking
+ // TestSSLContext.create.
TestSSLContext cWithWrongPrivateKeys;
{
- KeyManager originalKeyManager = testKeyStore.keyManagers[0];
- testKeyStore.keyManagers[0] =
- new RandomPrivateKeyX509ExtendedKeyManager(c.serverKeyManager);
+ // Create a RandomPrivateKeyX509ExtendedKeyManager based on the first
+ // X509ExtendedKeyManager in c.serverKeyManagers.
+ KeyManager randomPrivateKeyX509ExtendedKeyManager = null;
+ for (KeyManager keyManager : c.serverKeyManagers) {
+ if (keyManager instanceof X509ExtendedKeyManager) {
+ randomPrivateKeyX509ExtendedKeyManager =
+ new RandomPrivateKeyX509ExtendedKeyManager((X509ExtendedKeyManager) keyManager);
+ break;
+ }
+ }
+ if (randomPrivateKeyX509ExtendedKeyManager == null) {
+ fail("No X509ExtendedKeyManager in c.serverKeyManagers");
+ }
+
+ // Find the first X509ExtendedKeyManager in testKeyStore.keyManagers
+ int replaceIndex = -1;
+ for (int i = 0; i < testKeyStore.keyManagers.length; i++) {
+ KeyManager keyManager = testKeyStore.keyManagers[i];
+ if (keyManager instanceof X509ExtendedKeyManager) {
+ replaceIndex = i;
+ break;
+ }
+ }
+ if (replaceIndex == -1) {
+ fail("No X509ExtendedKeyManager in testKeyStore.keyManagers");
+ }
+
+ // Temporarily substitute the RandomPrivateKeyX509ExtendedKeyManager in place of the
+ // original X509ExtendedKeyManager.
+ KeyManager originalKeyManager = testKeyStore.keyManagers[replaceIndex];
+ testKeyStore.keyManagers[replaceIndex] = randomPrivateKeyX509ExtendedKeyManager;
cWithWrongPrivateKeys = TestSSLContext.create(testKeyStore, testKeyStore);
- testKeyStore.keyManagers[0] = originalKeyManager;
+ testKeyStore.keyManagers[replaceIndex] = originalKeyManager;
}
+ // To catch all the errors.
+ StringBuilder error = new StringBuilder();
+
String[] cipherSuites = c.clientContext.createSSLEngine().getSupportedCipherSuites();
for (String cipherSuite : cipherSuites) {
- boolean errorExpected = StandardNames.IS_RI && cipherSuite.endsWith("_SHA256");
try {
- /*
- * TLS_EMPTY_RENEGOTIATION_INFO_SCSV cannot be used on
- * its own, but instead in conjunction with other
- * cipher suites.
- */
- if (cipherSuite.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION)) {
- continue;
+ // Skip cipher suites that are obsoleted.
+ if (StandardNames.IS_RI && "TLSv1.2".equals(c.clientContext.getProtocol())
+ && StandardNames.CIPHER_SUITES_OBSOLETE_TLS12.contains(cipherSuite)) {
+ continue;
+ }
+ /*
+ * TLS_EMPTY_RENEGOTIATION_INFO_SCSV cannot be used on
+ * its own, but instead in conjunction with other
+ * cipher suites.
+ */
+ if (cipherSuite.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION)) {
+ continue;
+ }
+ /*
+ * Kerberos cipher suites require external setup. See "Kerberos Requirements" in
+ * https://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html
+ * #KRBRequire
+ */
+ if (cipherSuite.startsWith("TLS_KRB5_")) {
+ continue;
+ }
+
+ final String[] cipherSuiteArray
+ = (secureRenegotiation
+ ? new String[] { cipherSuite,
+ StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION }
+ : new String[] { cipherSuite });
+
+ // Check that handshake succeeds.
+ TestSSLEnginePair pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ client.setEnabledCipherSuites(cipherSuiteArray);
+ server.setEnabledCipherSuites(cipherSuiteArray);
}
- /*
- * Kerberos cipher suites require external setup. See "Kerberos Requirements" in
- * https://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html
- * #KRBRequire
- */
- if (cipherSuite.startsWith("TLS_KRB5_")) {
- continue;
- }
+ });
+ assertConnected(pair);
- final String[] cipherSuiteArray
- = (secureRenegotiation
- ? new String[] { cipherSuite,
- StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION }
- : new String[] { cipherSuite });
+ boolean needsRecordSplit =
+ ("TLS".equalsIgnoreCase(c.clientContext.getProtocol())
+ || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol()))
+ && cipherSuite.contains("_CBC_");
- // Check that handshake succeeds.
- TestSSLEnginePair pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
- @Override
- void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
- client.setEnabledCipherSuites(cipherSuiteArray);
- server.setEnabledCipherSuites(cipherSuiteArray);
- }
- });
- assertConnected(pair);
+ assertSendsCorrectly("This is the client. Hello!".getBytes(),
+ pair.client, pair.server, needsRecordSplit);
+ assertSendsCorrectly("This is the server. Hi!".getBytes(),
+ pair.server, pair.client, needsRecordSplit);
- boolean needsRecordSplit =
- ("TLS".equalsIgnoreCase(c.clientContext.getProtocol())
- || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol()))
- && cipherSuite.contains("_CBC_");
-
- assertSendsCorrectly("This is the client. Hello!".getBytes(),
- pair.client, pair.server, needsRecordSplit);
- assertSendsCorrectly("This is the server. Hi!".getBytes(),
- pair.server, pair.client, needsRecordSplit);
- assertFalse(errorExpected);
-
- // Check that handshake fails when the server does not possess the private key
- // corresponding to the server's certificate. This is achieved by using SSLContext
- // cWithWrongPrivateKeys whose KeyManager returns wrong private keys that match
- // the algorithm (and parameters) of the correct keys.
- if (!cipherSuite.contains("_anon_")) {
- // The identity of the server is verified only in non-anonymous key exchanges.
- try {
- TestSSLEnginePair p = TestSSLEnginePair.create(
- cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() {
- @Override
- void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
- client.setEnabledCipherSuites(cipherSuiteArray);
- server.setEnabledCipherSuites(cipherSuiteArray);
- }
- });
- assertConnected(p);
- fail("Handshake succeeded for " + cipherSuite
- + " despite server not having the correct private key");
- } catch (IOException expected) {}
- }
- } catch (Exception maybeExpected) {
- if (!errorExpected) {
- throw new Exception("Problem trying to connect cipher suite " + cipherSuite,
- maybeExpected);
- }
+ // Check that handshake fails when the server does not possess the private key
+ // corresponding to the server's certificate. This is achieved by using SSLContext
+ // cWithWrongPrivateKeys whose KeyManager returns wrong private keys that match
+ // the algorithm (and parameters) of the correct keys.
+ if (!cipherSuite.contains("_anon_")) {
+ // The identity of the server is verified only in non-anonymous key exchanges.
+ try {
+ TestSSLEnginePair p = TestSSLEnginePair.create(
+ cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ client.setEnabledCipherSuites(cipherSuiteArray);
+ server.setEnabledCipherSuites(cipherSuiteArray);
+ }
+ });
+ assertNotConnected(p);
+ } catch (IOException expected) {}
+ }
+ } catch (Exception e) {
+ String message = ("Problem trying to connect cipher suite " + cipherSuite);
+ System.out.println(message);
+ e.printStackTrace();
+ error.append(message);
+ error.append('\n');
}
}
c.close();
+
+ if (error.length() > 0) {
+ throw new Exception("One or more problems in "
+ + "test_SSLEngine_getSupportedCipherSuites_connect:\n" + error);
+ }
}
- private void assertSendsCorrectly(final byte[] sourceBytes, SSLEngine source, SSLEngine dest,
- boolean needsRecordSplit) throws SSLException {
+ private static void assertSendsCorrectly(final byte[] sourceBytes, SSLEngine source,
+ SSLEngine dest, boolean needsRecordSplit) throws SSLException {
ByteBuffer sourceOut = ByteBuffer.wrap(sourceBytes);
SSLSession sourceSession = source.getSession();
ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize());
@@ -297,9 +335,19 @@
e.setEnabledProtocols(e.getSupportedProtocols());
// Check that setEnabledProtocols affects getEnabledProtocols
- String[] protocols = new String[] { e.getSupportedProtocols()[0] };
- e.setEnabledProtocols(protocols);
- assertEquals(Arrays.asList(protocols), Arrays.asList(e.getEnabledProtocols()));
+ for (String protocol : e.getSupportedProtocols()) {
+ if ("SSLv2Hello".equals(protocol)) {
+ try {
+ e.setEnabledProtocols(new String[] { protocol });
+ fail("Should fail when SSLv2Hello is set by itself");
+ } catch (IllegalArgumentException expected) {}
+ } else {
+ String[] protocols = new String[] { protocol };
+ e.setEnabledProtocols(protocols);
+ assertEquals(Arrays.deepToString(protocols),
+ Arrays.deepToString(e.getEnabledProtocols()));
+ }
+ }
c.close();
}
@@ -355,17 +403,34 @@
}
public void test_SSLEngine_setUseClientMode() throws Exception {
+ boolean[] finished;
+
// client is client, server is server
- assertConnected(test_SSLEngine_setUseClientMode(true, false));
+ finished = new boolean[2];
+ assertConnected(test_SSLEngine_setUseClientMode(true, false, finished));
+ assertTrue(finished[0]);
+ assertTrue(finished[1]);
// client is server, server is client
- assertConnected(test_SSLEngine_setUseClientMode(false, true));
+ finished = new boolean[2];
+ assertConnected(test_SSLEngine_setUseClientMode(false, true, finished));
+ assertTrue(finished[0]);
+ assertTrue(finished[1]);
// both are client
- assertNotConnected(test_SSLEngine_setUseClientMode(true, true));
+ /*
+ * Our implementation throws an SSLHandshakeException, but RI just
+ * stalls forever
+ */
+ try {
+ assertNotConnected(test_SSLEngine_setUseClientMode(true, true, null));
+ assertTrue(StandardNames.IS_RI);
+ } catch (SSLHandshakeException maybeExpected) {
+ assertFalse(StandardNames.IS_RI);
+ }
// both are server
- assertNotConnected(test_SSLEngine_setUseClientMode(false, false));
+ assertNotConnected(test_SSLEngine_setUseClientMode(false, false, null));
}
public void test_SSLEngine_setUseClientMode_afterHandshake() throws Exception {
@@ -385,7 +450,8 @@
}
private TestSSLEnginePair test_SSLEngine_setUseClientMode(final boolean clientClientMode,
- final boolean serverClientMode)
+ final boolean serverClientMode,
+ final boolean[] finished)
throws Exception {
TestSSLContext c;
if (!clientClientMode && serverClientMode) {
@@ -400,7 +466,7 @@
client.setUseClientMode(clientClientMode);
server.setUseClientMode(serverClientMode);
}
- });
+ }, finished);
}
public void test_SSLEngine_clientAuth() throws Exception {
@@ -490,6 +556,20 @@
}
}
+ public void test_SSLEngine_endpointVerification_Success() throws Exception {
+ TestSSLContext c = TestSSLContext.create();
+ TestSSLEnginePair p = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ SSLParameters p = client.getSSLParameters();
+ p.setEndpointIdentificationAlgorithm("HTTPS");
+ client.setSSLParameters(p);
+ }
+ });
+ assertConnected(p);
+ c.close();
+ }
+
public void test_SSLEngine_getEnableSessionCreation() throws Exception {
TestSSLContext c = TestSSLContext.create();
SSLEngine e = c.clientContext.createSSLEngine();
@@ -498,13 +578,19 @@
}
public void test_SSLEngine_setEnableSessionCreation_server() throws Exception {
- TestSSLEnginePair p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
- @Override
- void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
- server.setEnableSessionCreation(false);
- }
- });
- assertNotConnected(p);
+ try {
+ TestSSLEnginePair p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ server.setEnableSessionCreation(false);
+ }
+ });
+ // For some reason, the RI doesn't throw an SSLException.
+ assertTrue(StandardNames.IS_RI);
+ assertNotConnected(p);
+ } catch (SSLException maybeExpected) {
+ assertFalse(StandardNames.IS_RI);
+ }
}
public void test_SSLEngine_setEnableSessionCreation_client() throws Exception {
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
index 501216f..2911b48 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -38,6 +38,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import javax.net.ServerSocketFactory;
import javax.net.ssl.HandshakeCompletedEvent;
@@ -245,9 +246,19 @@
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
// Check that setEnabledProtocols affects getEnabledProtocols
- String[] protocols = new String[] { ssl.getSupportedProtocols()[0] };
- ssl.setEnabledProtocols(protocols);
- assertEquals(Arrays.asList(protocols), Arrays.asList(ssl.getEnabledProtocols()));
+ for (String protocol : ssl.getSupportedProtocols()) {
+ if ("SSLv2Hello".equals(protocol)) {
+ try {
+ ssl.setEnabledProtocols(new String[] { protocol });
+ fail("Should fail when SSLv2Hello is set by itself");
+ } catch (IllegalArgumentException expected) {}
+ } else {
+ String[] protocols = new String[] { protocol };
+ ssl.setEnabledProtocols(protocols);
+ assertEquals(Arrays.deepToString(protocols),
+ Arrays.deepToString(ssl.getEnabledProtocols()));
+ }
+ }
}
public void test_SSLSocket_getSession() throws Exception {
@@ -258,6 +269,13 @@
assertFalse(session.isValid());
}
+ public void test_SSLSocket_getHandshakeSession() throws Exception {
+ SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket ssl = (SSLSocket) sf.createSocket();
+ SSLSession session = ssl.getHandshakeSession();
+ assertNull(session);
+ }
+
public void test_SSLSocket_startHandshake() throws Exception {
final TestSSLContext c = TestSSLContext.create();
SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
@@ -268,6 +286,7 @@
@Override public Void call() throws Exception {
server.startHandshake();
assertNotNull(server.getSession());
+ assertNull(server.getHandshakeSession());
try {
server.getSession().getPeerCertificates();
fail();
@@ -478,6 +497,9 @@
assertNotNull(socket);
assertSame(client, socket);
+ assertTrue(socket instanceof SSLSocket);
+ assertNull(((SSLSocket) socket).getHandshakeSession());
+
synchronized (handshakeCompletedListenerCalled) {
handshakeCompletedListenerCalled[0] = true;
handshakeCompletedListenerCalled.notify();
@@ -651,9 +673,8 @@
@Override public Void call() throws Exception {
try {
server.startHandshake();
- assertFalse(StandardNames.IS_RI);
+ fail();
} catch (SSLHandshakeException expected) {
- assertTrue(StandardNames.IS_RI);
}
return null;
}
@@ -798,7 +819,10 @@
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Void> future = executor.submit(new Callable<Void>() {
@Override public Void call() throws Exception {
- server.startHandshake();
+ try {
+ server.startHandshake();
+ } catch (SSLHandshakeException expected) {
+ }
return null;
}
});
@@ -903,6 +927,14 @@
assertEquals(p.getWantClientAuth(), ssl.getWantClientAuth());
assertEquals(p.getNeedClientAuth(), ssl.getNeedClientAuth());
+
+ assertNull(p.getEndpointIdentificationAlgorithm());
+ p.setEndpointIdentificationAlgorithm(null);
+ assertNull(p.getEndpointIdentificationAlgorithm());
+ p.setEndpointIdentificationAlgorithm("HTTPS");
+ assertEquals("HTTPS", p.getEndpointIdentificationAlgorithm());
+ p.setEndpointIdentificationAlgorithm("FOO");
+ assertEquals("FOO", p.getEndpointIdentificationAlgorithm());
}
public void test_SSLSocket_setSSLParameters() throws Exception {
@@ -1086,6 +1118,84 @@
server.close();
}
+ public void test_SSLSocket_endpointIdentification_Success() throws Exception {
+ final TestSSLContext c = TestSSLContext.create();
+ SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
+ SSLParameters p = client.getSSLParameters();
+ p.setEndpointIdentificationAlgorithm("HTTPS");
+ client.connect(new InetSocketAddress(c.host, c.port));
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ server.startHandshake();
+ assertNotNull(server.getSession());
+ try {
+ server.getSession().getPeerCertificates();
+ fail();
+ } catch (SSLPeerUnverifiedException expected) {
+ }
+ Certificate[] localCertificates = server.getSession().getLocalCertificates();
+ assertNotNull(localCertificates);
+ TestKeyStore.assertChainLength(localCertificates);
+ assertNotNull(localCertificates[0]);
+ TestSSLContext.assertCertificateInKeyStore(localCertificates[0],
+ c.serverKeyStore);
+ return null;
+ }
+ });
+ executor.shutdown();
+ client.startHandshake();
+ assertNotNull(client.getSession());
+ assertNull(client.getSession().getLocalCertificates());
+ Certificate[] peerCertificates = client.getSession().getPeerCertificates();
+ assertNotNull(peerCertificates);
+ TestKeyStore.assertChainLength(peerCertificates);
+ assertNotNull(peerCertificates[0]);
+ TestSSLContext.assertCertificateInKeyStore(peerCertificates[0], c.serverKeyStore);
+ future.get();
+ client.close();
+ server.close();
+ c.close();
+ }
+
+ public void test_SSLSocket_endpointIdentification_Failure() throws Exception {
+
+ final TestSSLContext c = TestSSLContext.create();
+ SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+ InetAddress.getByName("127.0.0.2"), c.port);
+ SSLParameters p = client.getSSLParameters();
+ p.setEndpointIdentificationAlgorithm("HTTPS");
+ client.setSSLParameters(p);
+ // client.connect(new InetSocketAddress(c.host, c.port));
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try {
+ server.startHandshake();
+ fail("Should receive SSLHandshakeException as server");
+ } catch (SSLHandshakeException expected) {
+ }
+ return null;
+ }
+ });
+ executor.shutdown();
+ try {
+ client.startHandshake();
+ fail("Should throw when hostname does not match expected");
+ } catch (SSLHandshakeException expected) {
+ } finally {
+ try {
+ future.get();
+ } finally {
+ client.close();
+ server.close();
+ c.close();
+ }
+ }
+ }
+
public void test_SSLSocket_setSoTimeout_basic() throws Exception {
ServerSocket listening = new ServerSocket(0);
@@ -1320,7 +1430,7 @@
/**
* b/7014266 Test to confirm that an SSLSocket.close() on one
- * thread will interupt another thread blocked reading on the same
+ * thread will interrupt another thread blocked reading on the same
* socket.
*/
public void test_SSLSocket_interrupt_read() throws Exception {
@@ -1331,7 +1441,16 @@
c.host.getHostName(),
c.port,
false);
- ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ // Create our own thread group so we can inspect the stack state later.
+ final ThreadGroup clientGroup = new ThreadGroup("client");
+ ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(clientGroup, r);
+ }
+ });
+
Future<Void> clientFuture = executor.submit(new Callable<Void>() {
@Override public Void call() throws Exception {
try {
@@ -1349,6 +1468,26 @@
SSLSocket server = (SSLSocket) c.serverSocket.accept();
server.startHandshake();
+
+ /*
+ * Wait for the client to at least be in the "read" method before
+ * calling close()
+ */
+ Thread[] threads = new Thread[1];
+ clientGroup.enumerate(threads);
+ if (threads[0] != null) {
+ boolean clientInRead = false;
+ while (!clientInRead) {
+ StackTraceElement[] elements = threads[0].getStackTrace();
+ for (StackTraceElement element : elements) {
+ if ("read".equals(element.getMethodName())) {
+ clientInRead = true;
+ break;
+ }
+ }
+ }
+ }
+
wrapping.close();
clientFuture.get();
server.close();
diff --git a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
index 7ee5778..571aa9c 100644
--- a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
+++ b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
@@ -57,20 +57,21 @@
*/
public void testGetName() throws Exception {
CertificateFactory certFactBC = CertificateFactory.getInstance("X.509", "BC");
- CertificateFactory certFactDRL = CertificateFactory.getInstance("X.509", "DRLCertFactory");
+ CertificateFactory certFactOpenSSL = CertificateFactory.getInstance("X.509",
+ "AndroidOpenSSL");
X509Certificate certBC = (X509Certificate)
certFactBC.generateCertificate(new ByteArrayInputStream(T61STRING_CERT));
- X509Certificate certDRL = (X509Certificate)
- certFactDRL.generateCertificate(new ByteArrayInputStream(T61STRING_CERT));
+ X509Certificate certOpenSSL = (X509Certificate)
+ certFactOpenSSL.generateCertificate(new ByteArrayInputStream(T61STRING_CERT));
- assertEquals(certBC, certDRL);
+ assertEquals(certBC, certOpenSSL);
assertEquals(certBC.getSubjectX500Principal(), certBC.getSubjectX500Principal());
- assertEquals(certDRL.getIssuerX500Principal(), certDRL.getIssuerX500Principal());
+ assertEquals(certOpenSSL.getIssuerX500Principal(), certOpenSSL.getIssuerX500Principal());
- assertEquals(certBC.getSubjectX500Principal(), certDRL.getSubjectX500Principal());
- assertEquals(certBC.getIssuerX500Principal(), certDRL.getIssuerX500Principal());
+ assertEquals(certBC.getSubjectX500Principal(), certOpenSSL.getSubjectX500Principal());
+ assertEquals(certBC.getIssuerX500Principal(), certOpenSSL.getIssuerX500Principal());
String[] formats = {
X500Principal.CANONICAL,
@@ -79,9 +80,9 @@
};
for (String format : formats) {
assertEquals(certBC.getSubjectX500Principal().getName(format),
- certDRL.getSubjectX500Principal().getName(format));
+ certOpenSSL.getSubjectX500Principal().getName(format));
assertEquals(certBC.getIssuerX500Principal().getName(format),
- certDRL.getIssuerX500Principal().getName(format));
+ certOpenSSL.getIssuerX500Principal().getName(format));
}
String expected = ""
+ "cn=entrust.net certification authority (2048),"
diff --git a/luni/src/test/java/libcore/xml/XmlPullParserFactoryTest.java b/luni/src/test/java/libcore/xml/XmlPullParserFactoryTest.java
index ea795f8..7194414 100644
--- a/luni/src/test/java/libcore/xml/XmlPullParserFactoryTest.java
+++ b/luni/src/test/java/libcore/xml/XmlPullParserFactoryTest.java
@@ -16,19 +16,359 @@
package libcore.xml;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
import junit.framework.TestCase;
+import org.kxml2.io.KXmlParser;
+import org.kxml2.io.KXmlSerializer;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
public class XmlPullParserFactoryTest extends TestCase {
- public void testNewInstance() throws Exception {
+ public void testDefaultNewInstance() throws Exception {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance(null, null);
XmlPullParser parser = factory.newPullParser();
XmlSerializer serializer = factory.newSerializer();
assertNotNull(parser);
assertNotNull(serializer);
+ assertTrue(parser instanceof KXmlParser);
+ assertTrue(serializer instanceof KXmlSerializer);
+ }
+
+ /**
+ * Tests that trying to instantiate a parser with an empty list of
+ * parsers and serializers fails.
+ */
+ public void testOverriding_emptyClassList() {
+ TestXmlPullParserFactory tf = new TestXmlPullParserFactory(null, null);
+
+ try {
+ tf.newPullParser();
+ fail();
+ } catch (XmlPullParserException expected) {
+ }
+
+ try {
+ tf.newPullParser();
+ fail();
+ } catch (XmlPullParserException expected) {
+ }
+ }
+
+ public void testOverriding_customClassList() throws Exception {
+ TestXmlPullParserFactory tf = new TestXmlPullParserFactory(
+ new String[] { "libcore.xml.XmlPullParserFactoryTest$XmlPullParserStub" },
+ new String[] { "libcore.xml.XmlPullParserFactoryTest$XmlSerializerStub" });
+
+ assertTrue(tf.newPullParser() instanceof XmlPullParserStub);
+ assertTrue(tf.newSerializer() instanceof XmlSerializerStub);
+
+ // Also check that we ignore instantiation errors as long as
+ // at least one parser / serializer is instantiable.
+ tf = new TestXmlPullParserFactory(
+ new String[] {
+ "libcore.xml.XmlPullParserFactoryTest$InaccessibleXmlParser",
+ "libcore.xml.XmlPullParserFactoryTest$XmlPullParserStub" },
+ new String[] {
+ "libcore.xml.XmlPullParserFactoryTest$InaccessibleXmlSerializer",
+ "libcore.xml.XmlPullParserFactoryTest$XmlSerializerStub" });
+
+ assertTrue(tf.newPullParser() instanceof XmlPullParserStub);
+ assertTrue(tf.newSerializer() instanceof XmlSerializerStub);
+ }
+
+ // https://b/12956724
+ public void testSetFeature_setsFeatureOnlyIfTrue() throws Exception {
+ TestXmlPullParserFactory tf = new TestXmlPullParserFactory(
+ new String[] { "libcore.xml.XmlPullParserFactoryTest$XmlParserThatHatesAllFeatures" }, null);
+
+ tf.setFeature("foo", false);
+ tf.newPullParser();
+ }
+
+
+ /**
+ * A class that makes use of inherited XmlPullParserFactory fields to check they are
+ * supported.
+ */
+ static final class TestXmlPullParserFactory extends XmlPullParserFactory {
+ TestXmlPullParserFactory(String[] parserClassList, String[] serializerClassList) {
+ super();
+ parserClasses.remove(0);
+ serializerClasses.remove(0);
+
+ try {
+ if (parserClassList != null) {
+ for (String parserClass : parserClassList) {
+ parserClasses.add(Class.forName(parserClass));
+ }
+ }
+
+ if (serializerClassList != null) {
+ for (String serializerClass : serializerClassList) {
+ serializerClasses.add(Class.forName(serializerClass));
+ }
+ }
+ } catch (ClassNotFoundException ignored) {
+ throw new AssertionError(ignored);
+ }
+ }
+ }
+
+ public static final class XmlParserThatHatesAllFeatures extends XmlPullParserStub {
+ @Override
+ public void setFeature(String name, boolean state) {
+ fail();
+ }
+ }
+
+ static final class InaccessibleXmlSerializer extends XmlSerializerStub {
+ }
+
+ static final class InaccessibleXmlParser extends XmlPullParserStub {
+ }
+
+ public static class XmlSerializerStub implements XmlSerializer {
+
+ public void setFeature(String name, boolean state) throws IllegalArgumentException,
+ IllegalStateException {
+ }
+
+ public boolean getFeature(String name) {
+ return false;
+ }
+
+ public void setProperty(String name, Object value) {
+ }
+
+ public Object getProperty(String name) {
+ return null;
+ }
+
+ public void setOutput(OutputStream os, String encoding) throws IOException {
+ }
+
+ public void setOutput(Writer writer) throws IOException {
+ }
+
+ public void startDocument(String encoding, Boolean standalone) throws IOException {
+ }
+
+ public void endDocument() throws IOException {
+ }
+
+ public void setPrefix(String prefix, String namespace) throws IOException {
+ }
+
+ public String getPrefix(String namespace, boolean generatePrefix) throws IllegalArgumentException {
+ return null;
+ }
+
+ public int getDepth() {
+ return 0;
+ }
+
+ public String getNamespace() {
+ return null;
+ }
+
+ public String getName() {
+ return null;
+ }
+
+ public XmlSerializer startTag(String namespace, String name) throws IOException {
+ return null;
+ }
+
+ public XmlSerializer attribute(String namespace, String name, String value) throws IOException {
+ return null;
+ }
+
+ public XmlSerializer endTag(String namespace, String name) throws IOException {
+ return null;
+ }
+
+ public XmlSerializer text(String text) throws IOException {
+ return null;
+ }
+
+ public XmlSerializer text(char[] buf, int start, int len) throws IOException {
+ return null;
+ }
+
+ public void cdsect(String text) throws IOException {
+ }
+
+ public void entityRef(String text) throws IOException {
+ }
+
+ public void processingInstruction(String text) throws IOException {
+ }
+
+ public void comment(String text) throws IOException {
+ }
+
+ public void docdecl(String text) throws IOException {
+ }
+
+ public void ignorableWhitespace(String text) throws IOException {
+ }
+
+ public void flush() throws IOException {
+ }
+ }
+
+ public static class XmlPullParserStub implements XmlPullParser {
+ public void setFeature(String name, boolean state) throws XmlPullParserException {
+ }
+
+ public boolean getFeature(String name) {
+ return false;
+ }
+
+ public void setProperty(String name, Object value) throws XmlPullParserException {
+ }
+
+ public Object getProperty(String name) {
+ return null;
+ }
+
+ public void setInput(Reader in) throws XmlPullParserException {
+ }
+
+ public void setInput(InputStream inputStream, String inputEncoding)
+ throws XmlPullParserException {
+ }
+
+ public String getInputEncoding() {
+ return null;
+ }
+
+ public void defineEntityReplacementText(String entityName, String replacementText)
+ throws XmlPullParserException {
+ }
+
+ public int getNamespaceCount(int depth) throws XmlPullParserException {
+ return 0;
+ }
+
+ public String getNamespacePrefix(int pos) throws XmlPullParserException {
+ return null;
+ }
+
+ public String getNamespaceUri(int pos) throws XmlPullParserException {
+ return null;
+ }
+
+ public String getNamespace(String prefix) {
+ return null;
+ }
+
+ public int getDepth() {
+ return 0;
+ }
+
+ public String getPositionDescription() {
+ return null;
+ }
+
+ public int getLineNumber() {
+ return 0;
+ }
+
+ public int getColumnNumber() {
+ return 0;
+ }
+
+ public boolean isWhitespace() throws XmlPullParserException {
+ return false;
+ }
+
+ public String getText() {
+ return null;
+ }
+
+ public char[] getTextCharacters(int[] holderForStartAndLength) {
+ return null;
+ }
+
+ public String getNamespace() {
+ return null;
+ }
+
+ public String getName() {
+ return null;
+ }
+
+ public String getPrefix() {
+ return null;
+ }
+
+ public boolean isEmptyElementTag() throws XmlPullParserException {
+ return false;
+ }
+
+ public int getAttributeCount() {
+ return 0;
+ }
+
+ public String getAttributeNamespace(int index) {
+ return null;
+ }
+
+ public String getAttributeName(int index) {
+ return null;
+ }
+
+ public String getAttributePrefix(int index) {
+ return null;
+ }
+
+ public String getAttributeType(int index) {
+ return null;
+ }
+
+ public boolean isAttributeDefault(int index) {
+ return false;
+ }
+
+ public String getAttributeValue(int index) {
+ return null;
+ }
+
+ public String getAttributeValue(String namespace, String name) {
+ return null;
+ }
+
+ public int getEventType() throws XmlPullParserException {
+ return 0;
+ }
+
+ public int next() throws XmlPullParserException, IOException {
+ return 0;
+ }
+
+ public int nextToken() throws XmlPullParserException, IOException {
+ return 0;
+ }
+
+ public void require(int type, String namespace, String name)
+ throws XmlPullParserException, IOException {
+ }
+
+ public String nextText() throws XmlPullParserException, IOException {
+ return null;
+ }
+
+ public int nextTag() throws XmlPullParserException, IOException {
+ return 0;
+ }
}
}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/KeyAgreementTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/KeyAgreementTest.java
index 99d127a..0642229 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/KeyAgreementTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/KeyAgreementTest.java
@@ -22,6 +22,8 @@
package org.apache.harmony.crypto.tests.javax.crypto;
+import org.apache.harmony.security.tests.support.SpiEngUtils;
+import org.apache.harmony.security.tests.support.TestKeyPair;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -31,6 +33,7 @@
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
+import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.DSAParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
@@ -40,12 +43,10 @@
import javax.crypto.ShortBufferException;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.spec.DHParameterSpec;
-
-import org.apache.harmony.crypto.tests.support.MyKeyAgreementSpi;
-import org.apache.harmony.security.tests.support.SpiEngUtils;
-import org.apache.harmony.security.tests.support.TestKeyPair;
-
import junit.framework.TestCase;
+import libcore.java.security.StandardNames;
+import libcore.javax.crypto.MockKey;
+import libcore.javax.crypto.MockKey2;
/**
@@ -676,4 +677,127 @@
//expected
}
}
+
+ private static abstract class MockProvider extends Provider {
+ public MockProvider(String name) {
+ super(name, 1.0, "Mock provider used for testing");
+ setup();
+ }
+
+ public abstract void setup();
+ }
+
+ public void testKeyAgreement_getInstance_SuppliedProviderNotRegistered_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ {
+ KeyAgreement s = KeyAgreement.getInstance("FOO", mockProvider);
+ s.init(new MockKey());
+ assertEquals(mockProvider, s.getProvider());
+ }
+ }
+
+ public void testKeyAgreement_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ {
+ Provider mockProvider2 = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ }
+ };
+ KeyAgreement s = KeyAgreement.getInstance("FOO", mockProvider2);
+ assertEquals(mockProvider2, s.getProvider());
+ }
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
+ public void testKeyAgreement_getInstance_DelayedInitialization_KeyType() throws Exception {
+ Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.SpecificKeyTypes.class.getName());
+ put("KeyAgreement.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+ Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.SpecificKeyTypes2.class.getName());
+ put("KeyAgreement.FOO SupportedKeyClasses", MockKey2.class.getName());
+ }
+ };
+ Provider mockProviderAll = new MockProvider("MockProviderAll") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderSpecific);
+ Security.addProvider(mockProviderSpecific2);
+ Security.addProvider(mockProviderAll);
+
+ try {
+ {
+ KeyAgreement s = KeyAgreement.getInstance("FOO");
+ s.init(new MockKey());
+ assertEquals(mockProviderSpecific, s.getProvider());
+
+ try {
+ s.init(new MockKey2());
+ assertEquals(mockProviderSpecific2, s.getProvider());
+ if (StandardNames.IS_RI) {
+ fail("RI was broken before; fix tests now that it works!");
+ }
+ } catch (InvalidKeyException e) {
+ if (!StandardNames.IS_RI) {
+ fail("Non-RI should select the right provider");
+ }
+ }
+ }
+
+ {
+ KeyAgreement s = KeyAgreement.getInstance("FOO");
+ s.init(new PrivateKey() {
+ @Override
+ public String getAlgorithm() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public String getFormat() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+ });
+ assertEquals(mockProviderAll, s.getProvider());
+ }
+
+ {
+ KeyAgreement s = KeyAgreement.getInstance("FOO");
+ assertEquals(mockProviderSpecific, s.getProvider());
+ }
+ } finally {
+ Security.removeProvider(mockProviderSpecific.getName());
+ Security.removeProvider(mockProviderSpecific2.getName());
+ Security.removeProvider(mockProviderAll.getName());
+ }
+ }
+
}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
index aaf2a15..ddd0695 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
@@ -27,27 +27,26 @@
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.spec.PSSParameterSpec;
import java.util.ArrayList;
import java.util.Arrays;
-
import javax.crypto.Mac;
import javax.crypto.MacSpi;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.DHGenParameterSpec;
-
import javax.crypto.spec.SecretKeySpec;
-
import org.apache.harmony.crypto.tests.support.MyMacSpi;
import org.apache.harmony.security.tests.support.SpiEngUtils;
-
import junit.framework.TestCase;
-
import junit.framework.Test;
import junit.framework.TestSuite;
+import libcore.java.security.StandardNames;
+import libcore.javax.crypto.MockKey;
+import libcore.javax.crypto.MockKey2;
/**
* Tests for Mac class constructors and methods
@@ -766,15 +765,14 @@
}
MacSpi spi = new MyMacSpi();
Mac mac = new myMac(spi, defaultProvider, defaultAlgorithm);
- assertEquals("Incorrect algorithm", mac.getAlgorithm(),
- defaultAlgorithm);
- assertEquals("Incorrect provider", mac.getProvider(), defaultProvider);
+ assertEquals("Incorrect algorithm", defaultAlgorithm, mac.getAlgorithm());
+ assertEquals("Incorrect provider", defaultProvider, mac.getProvider());
try {
mac.init(null, null);
fail("Exception should be thrown because init(..) uses incorrect parameters");
} catch (Exception e) {
}
- assertEquals("Invalid mac length", mac.getMacLength(), 0);
+ assertEquals("Invalid mac length", 0, mac.getMacLength());
mac = new myMac(null, null, null);
assertNull("Algorithm must be null", mac.getAlgorithm());
@@ -877,6 +875,127 @@
}
}
+ private static abstract class MockProvider extends Provider {
+ public MockProvider(String name) {
+ super(name, 1.0, "Mock provider used for testing");
+ setup();
+ }
+
+ public abstract void setup();
+ }
+
+ public void testMac_getInstance_SuppliedProviderNotRegistered_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ {
+ Mac s = Mac.getInstance("FOO", mockProvider);
+ s.init(new MockKey());
+ assertEquals(mockProvider, s.getProvider());
+ }
+ }
+
+ public void testMac_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ {
+ Provider mockProvider2 = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+ }
+ };
+ Mac s = Mac.getInstance("FOO", mockProvider2);
+ assertEquals(mockProvider2, s.getProvider());
+ }
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
+ public void testMac_getInstance_DelayedInitialization_KeyType() throws Exception {
+ Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.SpecificKeyTypes.class.getName());
+ put("Mac.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+ Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.SpecificKeyTypes2.class.getName());
+ put("Mac.FOO SupportedKeyClasses", MockKey2.class.getName());
+ }
+ };
+ Provider mockProviderAll = new MockProvider("MockProviderAll") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderSpecific);
+ Security.addProvider(mockProviderSpecific2);
+ Security.addProvider(mockProviderAll);
+
+ try {
+ {
+ Mac s = Mac.getInstance("FOO");
+ s.init(new MockKey());
+ assertEquals(mockProviderSpecific, s.getProvider());
+
+ try {
+ s.init(new MockKey2());
+ assertEquals(mockProviderSpecific2, s.getProvider());
+ if (StandardNames.IS_RI) {
+ fail("RI was broken before; fix tests now that it works!");
+ }
+ } catch (InvalidKeyException e) {
+ if (!StandardNames.IS_RI) {
+ fail("Non-RI should select the right provider");
+ }
+ }
+ }
+
+ {
+ Mac s = Mac.getInstance("FOO");
+ s.init(new PrivateKey() {
+ @Override
+ public String getAlgorithm() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public String getFormat() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+ });
+ assertEquals(mockProviderAll, s.getProvider());
+ }
+
+ {
+ Mac s = Mac.getInstance("FOO");
+ assertEquals(mockProviderSpecific, s.getProvider());
+ }
+ } finally {
+ Security.removeProvider(mockProviderSpecific.getName());
+ Security.removeProvider(mockProviderSpecific2.getName());
+ Security.removeProvider(mockProviderAll.getName());
+ }
+ }
+
public static Test suite() {
return new TestSuite(MacTest.class);
}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockKeyAgreementSpi.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockKeyAgreementSpi.java
new file mode 100644
index 0000000..4b53a6b
--- /dev/null
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockKeyAgreementSpi.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 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 org.apache.harmony.crypto.tests.javax.crypto;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import libcore.javax.crypto.MockKey;
+import libcore.javax.crypto.MockKey2;
+
+public class MockKeyAgreementSpi extends KeyAgreementSpi {
+ public static class SpecificKeyTypes extends MockKeyAgreementSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockKey)) {
+ throw new InvalidKeyException("Must be MockKey!");
+ }
+ }
+ }
+
+ public static class SpecificKeyTypes2 extends MockKeyAgreementSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockKey2)) {
+ throw new InvalidKeyException("Must be MockKey2!");
+ }
+ }
+ }
+
+ public static class AllKeyTypes extends MockKeyAgreementSpi {
+ }
+
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ }
+
+ @Override
+ protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException,
+ IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected byte[] engineGenerateSecret() throws IllegalStateException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+ throws IllegalStateException, ShortBufferException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException,
+ NoSuchAlgorithmException, InvalidKeyException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+ checkKeyType(key);
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ checkKeyType(key);
+ }
+}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockMacSpi.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockMacSpi.java
new file mode 100644
index 0000000..6a28fb3e
--- /dev/null
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockMacSpi.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 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 org.apache.harmony.crypto.tests.javax.crypto;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.MacSpi;
+import libcore.javax.crypto.MockKey;
+import libcore.javax.crypto.MockKey2;
+
+public class MockMacSpi extends MacSpi {
+ public static class SpecificKeyTypes extends MockMacSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockKey)) {
+ throw new InvalidKeyException("Must be MockKey!");
+ }
+ }
+ }
+
+ public static class SpecificKeyTypes2 extends MockMacSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockKey2)) {
+ throw new InvalidKeyException("Must be MockKey2!");
+ }
+ }
+ }
+
+ public static class AllKeyTypes extends MockMacSpi {
+ }
+
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ }
+
+ @Override
+ protected int engineGetMacLength() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+ InvalidParameterException {
+ checkKeyType(key);
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int offset, int len) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected byte[] engineDoFinal() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineReset() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherAesTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherAesTest.java
index 0b8f017..85c2438 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherAesTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherAesTest.java
@@ -18,7 +18,7 @@
import junit.framework.TestCase;
public class CipherAesTest extends TestCase {
-// 216 cases checked
+// 324 cases checked
public void test_AesNoISO() {
CipherSymmetricKeyThread aesNoISO = new CipherSymmetricKeyThread("AES",
new int[] {128, 192, 256}, // Keysize must be 128, 192, 256.
@@ -29,7 +29,8 @@
"CFB128", "OFB", "OFB8", "OFB16", "OFB24", "OFB32",
"OFB40", "OFB48", "OFB56", "OFB64", "OFB72", "OFB80",
"OFB88", "OFB96", "OFB104", "OFB112", "OFB120",
- "OFB128"}, new String[] {"NoPadding", "PKCS5Padding"});
+ "OFB128"},
+ new String[] {"NoPadding", "PKCS5Padding", "PKCS7Padding"});
aesNoISO.launcher();
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherDESedeTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherDESedeTest.java
index d8565f1..c7b4f20 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherDESedeTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherDESedeTest.java
@@ -18,7 +18,7 @@
import junit.framework.TestCase;
public class CipherDESedeTest extends TestCase {
-// 80 cases checked
+// 120 cases checked
public void test_DESedeNoISO() {
CipherSymmetricKeyThread DESedeNoISO = new CipherSymmetricKeyThread(
"DESede", new int[] {112, 168},// Keysize must be 112 or 168.
@@ -26,7 +26,8 @@
"ECB", "CBC", "CFB", "CFB8", "CFB16", "CFB24", "CFB32",
"CFB40", "CFB48", "CFB56", "CFB64", "OFB", "OFB8",
"OFB16", "OFB24", "OFB32", "OFB40", "OFB48", "OFB56",
- "OFB64"}, new String[] {"NoPadding", "PKCS5Padding"});
+ "OFB64"},
+ new String[] {"NoPadding", "PKCS5Padding", "PKCS7Padding"});
DESedeNoISO.launcher();
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/support/MyCipher.java b/luni/src/test/java/org/apache/harmony/crypto/tests/support/MyCipher.java
index c9eb26e..ff4babc 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/support/MyCipher.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/support/MyCipher.java
@@ -52,7 +52,8 @@
@Override
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
- if (!"PKCS5Padding".equals(padding)) {
+ if ((!"PKCS5Padding".equals(padding))
+ && (!"PKCS7Padding".equals(padding))) {
throw new NoSuchPaddingException(padding);
}
}
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java
index c516f67..5258fd1 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java
@@ -17,6 +17,12 @@
package org.apache.harmony.luni.tests.internal.net.www.protocol.https;
+import com.google.mockwebserver.Dispatcher;
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
+import com.google.mockwebserver.SocketPolicy;
+
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -25,7 +31,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.PrintStream;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
@@ -36,20 +41,15 @@
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Arrays;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
+import java.util.Collections;
+import java.util.LinkedList;
+
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
@@ -59,22 +59,26 @@
/**
* Implementation independent test for HttpsURLConnection.
- * The test needs certstore file placed in system classpath
- * and named as "key_store." + the type of the
- * default KeyStore installed in the system in lower case.
- * <br>
- * For example: if default KeyStore type in the system is BKS
- * (i.e. java.security file sets up the property keystore.type=BKS),
- * thus classpath should point to the directory with "key_store.bks"
- * file.
- * <br>
- * This certstore file should contain self-signed certificate
- * generated by keytool utility in a usual way.
- * <br>
- * The password to the certstore should be "password" (without quotes).
*/
public class HttpsURLConnectionTest extends TestCase {
+ private static final String POST_METHOD = "POST";
+
+ private static final String GET_METHOD = "GET";
+
+ /**
+ * Data to be posted by client to the server when the method is POST.
+ */
+ private static final String POST_DATA = "_.-^ Client's Data ^-._";
+
+ /**
+ * The content of the response to be sent during HTTPS session.
+ */
+ private static final String RESPONSE_CONTENT
+ = "<HTML>\n"
+ + "<HEAD><TITLE>HTTPS Response Content</TITLE></HEAD>\n"
+ + "</HTML>";
+
// the password to the store
private static final String KS_PASSWORD = "password";
@@ -107,7 +111,7 @@
* Checks that HttpsURLConnection's default SSLSocketFactory is operable.
*/
public void testGetDefaultSSLSocketFactory() throws Exception {
- // set up the properties defining the default values needed by SSL stuff
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
SSLSocketFactory defaultSSLSF = HttpsURLConnection.getDefaultSSLSocketFactory();
@@ -119,55 +123,58 @@
}
public void testHttpsConnection() throws Throwable {
- // set up the properties defining the default values needed by SSL stuff
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSL server socket acting as a server
SSLContext ctx = getContext();
- ServerSocket ss = ctx.getServerSocketFactory().createServerSocket(0);
- // create the HostnameVerifier to check hostname verification
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
+
+ // create a webserver to check and respond to requests
+ SingleRequestDispatcher dispatcher = new SingleRequestDispatcher(GET_METHOD, OK_CODE);
+ MockWebServer webServer = createWebServer(ctx, dispatcher);
// create url connection to be tested
- URL url = new URL("https://localhost:" + ss.getLocalPort());
+ URL url = webServer.getUrl("/");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(ctx.getSocketFactory());
// perform the interaction between the peers
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
+ executeClientRequest(connection, false /* doOutput */);
- // check the connection state
- checkConnectionStateParameters(connection, peerSocket);
+ checkConnectionStateParameters(connection, dispatcher.getLastRequest());
// should silently exit
connection.connect();
+
+ webServer.shutdown();
}
/**
- * Tests the behaviour of HTTPS connection in case of unavailability
- * of requested resource.
+ * Tests the behaviour of HTTPS connection in case of unavailability of requested resource.
*/
public void testHttpsConnection_Not_Found_Response() throws Throwable {
- // set up the properties defining the default values needed by SSL stuff
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSL server socket acting as a server
SSLContext ctx = getContext();
- ServerSocket ss = ctx.getServerSocketFactory().createServerSocket(0);
- // create the HostnameVerifier to check hostname verification
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
+
+ // create a webserver to check and respond to requests
+ SingleRequestDispatcher dispatcher =
+ new SingleRequestDispatcher(GET_METHOD, NOT_FOUND_CODE);
+ MockWebServer webServer = createWebServer(ctx, dispatcher);
// create url connection to be tested
- URL url = new URL("https://localhost:" + ss.getLocalPort());
+ URL url = webServer.getUrl("/");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(ctx.getSocketFactory());
try {
- doInteraction(connection, ss, NOT_FOUND_CODE);
+ executeClientRequest(connection, false /* doOutput */);
fail("Expected exception was not thrown.");
} catch (FileNotFoundException e) {
if (DO_LOG) {
@@ -178,94 +185,100 @@
// should silently exit
connection.connect();
+
+ webServer.shutdown();
}
/**
- * Tests possibility to set up the default SSLSocketFactory
- * to be used by HttpsURLConnection.
+ * Tests possibility to set up the default SSLSocketFactory to be used by HttpsURLConnection.
*/
public void testSetDefaultSSLSocketFactory() throws Throwable {
- // create the SSLServerSocket which will be used by server side
- SSLContext ctx = getContext();
- SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(0);
+ // set up the properties pointing to the key/trust stores
+ setUpStoreProperties();
- SSLSocketFactory socketFactory = (SSLSocketFactory) ctx.getSocketFactory();
+ SSLContext ctx = getContext();
+
+ SSLSocketFactory socketFactory = ctx.getSocketFactory();
// set up the factory as default
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
// check the result
assertSame("Default SSLSocketFactory differs from expected",
- socketFactory, HttpsURLConnection.getDefaultSSLSocketFactory());
+ socketFactory, HttpsURLConnection.getDefaultSSLSocketFactory());
- // create the HostnameVerifier to check hostname verification
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // set the initial default host name verifier.
+ TestHostnameVerifier initialHostnameVerifier = new TestHostnameVerifier();
+ HttpsURLConnection.setDefaultHostnameVerifier(initialHostnameVerifier);
+
+ // create a webserver to check and respond to requests
+ SingleRequestDispatcher dispatcher = new SingleRequestDispatcher(GET_METHOD, OK_CODE);
+ MockWebServer webServer = createWebServer(ctx, dispatcher);
// create HttpsURLConnection to be tested
- URL url = new URL("https://localhost:" + ss.getLocalPort());
+ URL url = webServer.getUrl("/");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
- TestHostnameVerifier hnv_late = new TestHostnameVerifier();
- // late initialization: should not be used for created connection
- HttpsURLConnection.setDefaultHostnameVerifier(hnv_late);
+ // late initialization: this HostnameVerifier should not be used for created connection
+ TestHostnameVerifier lateHostnameVerifier = new TestHostnameVerifier();
+ HttpsURLConnection.setDefaultHostnameVerifier(lateHostnameVerifier);
// perform the interaction between the peers
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
- // check the connection state
- checkConnectionStateParameters(connection, peerSocket);
+ executeClientRequest(connection, false /* doOutput */);
+ checkConnectionStateParameters(connection, dispatcher.getLastRequest());
+
// check the verification process
- assertTrue("Hostname verification was not done", hnv.verified);
+ assertTrue("Hostname verification was not done", initialHostnameVerifier.verified);
assertFalse("Hostname verification should not be done by this verifier",
- hnv_late.verified);
+ lateHostnameVerifier.verified);
// check the used SSLSocketFactory
assertSame("Default SSLSocketFactory should be used",
- HttpsURLConnection.getDefaultSSLSocketFactory(),
- connection.getSSLSocketFactory());
+ HttpsURLConnection.getDefaultSSLSocketFactory(),
+ connection.getSSLSocketFactory());
- // should silently exit
- connection.connect();
+ webServer.shutdown();
}
/**
- * Tests possibility to set up the SSLSocketFactory
- * to be used by HttpsURLConnection.
+ * Tests
+ * {@link javax.net.ssl.HttpsURLConnection#setSSLSocketFactory(javax.net.ssl.SSLSocketFactory)}.
*/
public void testSetSSLSocketFactory() throws Throwable {
- // create the SSLServerSocket which will be used by server side
+ // set up the properties pointing to the key/trust stores
SSLContext ctx = getContext();
- SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(0);
- // create the HostnameVerifier to check hostname verification
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // set the initial default host name verifier.
+ TestHostnameVerifier hostnameVerifier = new TestHostnameVerifier();
+ HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
+
+ // create a webserver to check and respond to requests
+ SingleRequestDispatcher dispatcher = new SingleRequestDispatcher(GET_METHOD, OK_CODE);
+ MockWebServer webServer = createWebServer(ctx, dispatcher);
// create HttpsURLConnection to be tested
- URL url = new URL("https://localhost:" + ss.getLocalPort());
+ URL url = webServer.getUrl("/");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
- SSLSocketFactory socketFactory = (SSLSocketFactory) ctx.getSocketFactory();
+ // late initialization: should not be used for the created connection.
+ SSLSocketFactory socketFactory = ctx.getSocketFactory();
connection.setSSLSocketFactory(socketFactory);
- TestHostnameVerifier hnv_late = new TestHostnameVerifier();
// late initialization: should not be used for created connection
- HttpsURLConnection.setDefaultHostnameVerifier(hnv_late);
+ TestHostnameVerifier lateHostnameVerifier = new TestHostnameVerifier();
+ HttpsURLConnection.setDefaultHostnameVerifier(lateHostnameVerifier);
// perform the interaction between the peers
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
- // check the connection state
- checkConnectionStateParameters(connection, peerSocket);
+ executeClientRequest(connection, false /* doOutput */);
+ checkConnectionStateParameters(connection, dispatcher.getLastRequest());
// check the verification process
- assertTrue("Hostname verification was not done", hnv.verified);
+ assertTrue("Hostname verification was not done", hostnameVerifier.verified);
assertFalse("Hostname verification should not be done by this verifier",
- hnv_late.verified);
+ lateHostnameVerifier.verified);
// check the used SSLSocketFactory
assertNotSame("Default SSLSocketFactory should not be used",
- HttpsURLConnection.getDefaultSSLSocketFactory(),
- connection.getSSLSocketFactory());
- assertSame("Result differs from expected",
- socketFactory, connection.getSSLSocketFactory());
+ HttpsURLConnection.getDefaultSSLSocketFactory(),
+ connection.getSSLSocketFactory());
+ assertSame("Result differs from expected", socketFactory, connection.getSSLSocketFactory());
- // should silently exit
- connection.connect();
+ webServer.shutdown();
}
/**
@@ -304,97 +317,107 @@
* Tests if setHostnameVerifier() method replaces default verifier.
*/
public void testSetHostnameVerifier() throws Throwable {
- // setting up the properties pointing to the key/trust stores
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSLServerSocket which will be used by server side
- SSLServerSocket ss = (SSLServerSocket)
- getContext().getServerSocketFactory().createServerSocket(0);
+ SSLContext ctx = getContext();
- // create the HostnameVerifier to check that Hostname verification
- // is done
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ TestHostnameVerifier defaultHostnameVerifier = new TestHostnameVerifier();
+ HttpsURLConnection.setDefaultHostnameVerifier(defaultHostnameVerifier);
+
+ // create a webserver to check and respond to requests
+ SingleRequestDispatcher dispatcher = new SingleRequestDispatcher(GET_METHOD, OK_CODE);
+ MockWebServer webServer = createWebServer(ctx, dispatcher);
// create HttpsURLConnection to be tested
- URL url = new URL("https://localhost:" + ss.getLocalPort());
+ URL url = webServer.getUrl("/");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(getContext().getSocketFactory());
- TestHostnameVerifier hnv_late = new TestHostnameVerifier();
- // replace default verifier
- connection.setHostnameVerifier(hnv_late);
+ // replace the default verifier
+ TestHostnameVerifier connectionHostnameVerifier = new TestHostnameVerifier();
+ connection.setHostnameVerifier(connectionHostnameVerifier);
// perform the interaction between the peers and check the results
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
- assertTrue("Hostname verification was not done", hnv_late.verified);
+ executeClientRequest(connection, false /* doOutput */);
+ assertTrue("Hostname verification was not done", connectionHostnameVerifier.verified);
assertFalse("Hostname verification should not be done by this verifier",
- hnv.verified);
- checkConnectionStateParameters(connection, peerSocket);
+ defaultHostnameVerifier.verified);
- // should silently exit
- connection.connect();
+ checkConnectionStateParameters(connection, dispatcher.getLastRequest());
+
+ webServer.shutdown();
}
/**
* Tests the behaviour in case of sending the data to the server.
*/
public void test_doOutput() throws Throwable {
- // setting up the properties pointing to the key/trust stores
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSLServerSocket which will be used by server side
- SSLServerSocket ss = (SSLServerSocket)
- getContext().getServerSocketFactory().createServerSocket(0);
+ SSLContext ctx = getContext();
- // create the HostnameVerifier to check that Hostname verification
- // is done
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // create a webserver to check and respond to requests
+ SingleRequestDispatcher dispatcher = new SingleRequestDispatcher(POST_METHOD, OK_CODE);
+ MockWebServer webServer = createWebServer(ctx, dispatcher);
+
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
// create HttpsURLConnection to be tested
- URL url = new URL("https://localhost:" + ss.getLocalPort());
+ URL url = webServer.getUrl("/");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(getContext().getSocketFactory());
- connection.setDoOutput(true);
// perform the interaction between the peers and check the results
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
- checkConnectionStateParameters(connection, peerSocket);
+ executeClientRequest(connection, true /* doOutput */);
+ checkConnectionStateParameters(connection, dispatcher.getLastRequest());
// should silently exit
connection.connect();
+
+ webServer.shutdown();
}
/**
* Tests HTTPS connection process made through the proxy server.
*/
public void testProxyConnection() throws Throwable {
- // setting up the properties pointing to the key/trust stores
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSLServerSocket which will be used by server side
- ServerSocket ss = new ServerSocket(0);
+ SSLContext ctx = getContext();
- // create the HostnameVerifier to check that Hostname verification
- // is done
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
+
+ // create a server that pretends to be both a proxy and then the webserver
+ // request 1: proxy CONNECT, respond with OK
+ ProxyConnectDispatcher proxyConnectDispatcher =
+ new ProxyConnectDispatcher(false /* authenticationRequired */);
+ // request 2: tunnelled GET, respond with OK
+ SingleRequestDispatcher getDispatcher = new SingleRequestDispatcher(GET_METHOD, OK_CODE);
+ DelegatingDispatcher delegatingDispatcher =
+ new DelegatingDispatcher(proxyConnectDispatcher, getDispatcher);
+ MockWebServer proxyAndWebServer = createProxyAndWebServer(ctx, delegatingDispatcher);
// create HttpsURLConnection to be tested
+ URL proxyUrl = proxyAndWebServer.getUrl("/");
+ InetSocketAddress proxyAddress = new InetSocketAddress("localhost", proxyUrl.getPort());
URL url = new URL("https://requested.host:55556/requested.data");
HttpsURLConnection connection = (HttpsURLConnection)
- url.openConnection(new Proxy(Proxy.Type.HTTP,
- new InetSocketAddress("localhost",
- ss.getLocalPort())));
+ url.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddress));
connection.setSSLSocketFactory(getContext().getSocketFactory());
// perform the interaction between the peers and check the results
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
- checkConnectionStateParameters(connection, peerSocket);
+ executeClientRequest(connection, false /* doOutput */);
+ checkConnectionStateParameters(connection, getDispatcher.getLastRequest());
// should silently exit
connection.connect();
+
+ proxyAndWebServer.shutdown();
}
/**
@@ -402,81 +425,105 @@
* Proxy server needs authentication.
*/
public void testProxyAuthConnection() throws Throwable {
- // setting up the properties pointing to the key/trust stores
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSLServerSocket which will be used by server side
- ServerSocket ss = new ServerSocket(0);
+ SSLContext ctx = getContext();
- // create the HostnameVerifier to check that Hostname verification
- // is done
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
Authenticator.setDefault(new Authenticator() {
-
protected PasswordAuthentication getPasswordAuthentication() {
- return new PasswordAuthentication("user", "password"
- .toCharArray());
+ return new PasswordAuthentication("user", "password".toCharArray());
}
});
+ // create a server that pretends to be both a proxy and then the webserver
+ // request 1: proxy CONNECT, respond with auth challenge
+ ProxyConnectAuthFailDispatcher authFailDispatcher = new ProxyConnectAuthFailDispatcher();
+ // request 2: proxy CONNECT, respond with OK
+ ProxyConnectDispatcher proxyConnectDispatcher =
+ new ProxyConnectDispatcher(true /* authenticationRequired */);
+ // request 3: tunnelled GET, respond with OK
+ SingleRequestDispatcher getDispatcher = new SingleRequestDispatcher(GET_METHOD, OK_CODE);
+ DelegatingDispatcher delegatingDispatcher = new DelegatingDispatcher(
+ authFailDispatcher, proxyConnectDispatcher, getDispatcher);
+ MockWebServer proxyAndWebServer = createProxyAndWebServer(ctx, delegatingDispatcher);
+
// create HttpsURLConnection to be tested
+ URL proxyUrl = proxyAndWebServer.getUrl("/");
+ InetSocketAddress proxyAddress = new InetSocketAddress("localhost", proxyUrl.getPort());
URL url = new URL("https://requested.host:55555/requested.data");
HttpsURLConnection connection = (HttpsURLConnection)
- url.openConnection(new Proxy(Proxy.Type.HTTP,
- new InetSocketAddress("localhost",
- ss.getLocalPort())));
+ url.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddress));
connection.setSSLSocketFactory(getContext().getSocketFactory());
// perform the interaction between the peers and check the results
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
- checkConnectionStateParameters(connection, peerSocket);
+ executeClientRequest(connection, false /* doOutput */);
+ checkConnectionStateParameters(connection, getDispatcher.getLastRequest());
// should silently exit
connection.connect();
+
+ proxyAndWebServer.shutdown();
}
/**
* Tests HTTPS connection process made through the proxy server.
- * 2 HTTPS connections are opened for one URL. For the first time
- * the connection is opened through one proxy,
- * for the second time through another.
+ * Two HTTPS connections are opened for one URL: the first time the connection is opened
+ * through one proxy, the second time it is opened through another.
*/
public void testConsequentProxyConnection() throws Throwable {
- // setting up the properties pointing to the key/trust stores
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSLServerSocket which will be used by server side
- ServerSocket ss = new ServerSocket(0);
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
- // create the HostnameVerifier to check that Hostname verification
- // is done
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // create a server that pretends to be both a proxy and then the webserver
+ SingleRequestDispatcher getDispatcher1 = new SingleRequestDispatcher(GET_METHOD, OK_CODE);
+ MockWebServer proxyAndWebServer1 = createProxiedServer(getDispatcher1);
// create HttpsURLConnection to be tested
+ URL proxyUrl1 = proxyAndWebServer1.getUrl("/");
URL url = new URL("https://requested.host:55555/requested.data");
+ InetSocketAddress proxyAddress = new InetSocketAddress("localhost", proxyUrl1.getPort());
HttpsURLConnection connection = (HttpsURLConnection)
- url.openConnection(new Proxy(Proxy.Type.HTTP,
- new InetSocketAddress("localhost",
- ss.getLocalPort())));
+ url.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddress));
connection.setSSLSocketFactory(getContext().getSocketFactory());
+ executeClientRequest(connection, false /* doOutput */);
+ checkConnectionStateParameters(connection, getDispatcher1.getLastRequest());
+
+ proxyAndWebServer1.shutdown();
+
+ // create another server
+ SingleRequestDispatcher getDispatcher2 = new SingleRequestDispatcher(GET_METHOD, OK_CODE);
+ MockWebServer proxyAndWebServer2 = createProxiedServer(getDispatcher2);
+
+ // create another HttpsURLConnection to be tested
+ URL proxyUrl2 = proxyAndWebServer2.getUrl("/");
+ InetSocketAddress proxyAddress2 = new InetSocketAddress("localhost", proxyUrl2.getPort());
+ HttpsURLConnection connection2 = (HttpsURLConnection) url.openConnection(
+ new Proxy(Proxy.Type.HTTP, proxyAddress2));
+ connection2.setSSLSocketFactory(getContext().getSocketFactory());
// perform the interaction between the peers and check the results
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
- checkConnectionStateParameters(connection, peerSocket);
+ executeClientRequest(connection2, false /* doOutput */);
+ checkConnectionStateParameters(connection2, getDispatcher2.getLastRequest());
- // create another SSLServerSocket which will be used by server side
- ss = new ServerSocket(0);
+ proxyAndWebServer2.shutdown();
+ }
- connection = (HttpsURLConnection) url.openConnection(new Proxy(
- Proxy.Type.HTTP, new InetSocketAddress("localhost", ss.getLocalPort())));
- connection.setSSLSocketFactory(getContext().getSocketFactory());
-
- // perform the interaction between the peers and check the results
- peerSocket = (SSLSocket) doInteraction(connection, ss);
- checkConnectionStateParameters(connection, peerSocket);
+ private static MockWebServer createProxiedServer(Dispatcher getDispatcher)
+ throws Exception {
+ // request 1: proxy CONNECT, respond with OK
+ ProxyConnectDispatcher proxyConnectDispatcher =
+ new ProxyConnectDispatcher(false /* authenticationRequired */);
+ // request 2: The get dispatcher.
+ DelegatingDispatcher delegatingDispatcher1 =
+ new DelegatingDispatcher(proxyConnectDispatcher, getDispatcher);
+ return createProxyAndWebServer(getContext(), delegatingDispatcher1);
}
/**
@@ -485,37 +532,47 @@
* Client sends data to the server.
*/
public void testProxyAuthConnection_doOutput() throws Throwable {
- // setting up the properties pointing to the key/trust stores
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSLServerSocket which will be used by server side
- ServerSocket ss = new ServerSocket(0);
+ SSLContext ctx = getContext();
- // create the HostnameVerifier to check that Hostname verification
- // is done
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
Authenticator.setDefault(new Authenticator() {
-
protected PasswordAuthentication getPasswordAuthentication() {
- return new PasswordAuthentication("user", "password"
- .toCharArray());
+ return new PasswordAuthentication("user", "password".toCharArray());
}
});
+ // create a server that pretends to be both a proxy and then the webserver
+ // request 1: proxy CONNECT, respond with auth challenge
+ ProxyConnectAuthFailDispatcher authFailDispatcher = new ProxyConnectAuthFailDispatcher();
+ // request 2: proxy CONNECT, respond with OK
+ ProxyConnectDispatcher proxyConnectDispatcher =
+ new ProxyConnectDispatcher(true /* authenticationRequired */);
+ // request 3: tunnelled POST, respond with OK
+ SingleRequestDispatcher postDispatcher = new SingleRequestDispatcher(POST_METHOD, OK_CODE);
+ DelegatingDispatcher delegatingDispatcher = new DelegatingDispatcher(
+ authFailDispatcher, proxyConnectDispatcher, postDispatcher);
+ MockWebServer proxyAndWebServer = createProxyAndWebServer(ctx, delegatingDispatcher);
+ URL proxyUrl = proxyAndWebServer.getUrl("/");
+
// create HttpsURLConnection to be tested
- URL url = new URL("https://requested.host:55554/requested.data");
+ InetSocketAddress proxyAddress = new InetSocketAddress("localhost", proxyUrl.getPort());
HttpsURLConnection connection = (HttpsURLConnection)
- url.openConnection(new Proxy(Proxy.Type.HTTP,
- new InetSocketAddress("localhost",
- ss.getLocalPort())));
+ proxyUrl.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddress));
connection.setSSLSocketFactory(getContext().getSocketFactory());
- connection.setDoOutput(true);
// perform the interaction between the peers and check the results
- SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss, OK_CODE, true);
- checkConnectionStateParameters(connection, peerSocket);
+ executeClientRequest(connection, true /* doOutput */);
+ checkConnectionStateParameters(connection, postDispatcher.getLastRequest());
+
+ // should silently exit
+ connection.connect();
+
+ proxyAndWebServer.shutdown();
}
/**
@@ -524,79 +581,82 @@
* (Authenticator was not set up in the system).
*/
public void testProxyAuthConnectionFailed() throws Throwable {
- // setting up the properties pointing to the key/trust stores
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSLServerSocket which will be used by server side
- ServerSocket ss = new ServerSocket(0);
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
- // create the HostnameVerifier to check that Hostname verification
- // is done
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // create a server that pretends to be both a proxy that requests authentication.
+ MockWebServer proxyAndWebServer = new MockWebServer();
+ ProxyConnectAuthFailDispatcher authFailDispatcher = new ProxyConnectAuthFailDispatcher();
+ proxyAndWebServer.setDispatcher(authFailDispatcher);
+ proxyAndWebServer.play();
// create HttpsURLConnection to be tested
+ URL proxyUrl = proxyAndWebServer.getUrl("/");
+ InetSocketAddress proxyAddress = new InetSocketAddress("localhost", proxyUrl.getPort());
URL url = new URL("https://requested.host:55555/requested.data");
HttpsURLConnection connection = (HttpsURLConnection)
- url.openConnection(new Proxy(Proxy.Type.HTTP,
- new InetSocketAddress("localhost",
- ss.getLocalPort())));
+ url.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddress));
connection.setSSLSocketFactory(getContext().getSocketFactory());
// perform the interaction between the peers and check the results
try {
- doInteraction(connection, ss, AUTHENTICATION_REQUIRED_CODE, true);
+ executeClientRequest(connection, false);
} catch (IOException e) {
// SSL Tunnelling failed
if (DO_LOG) {
- System.out.println("Got expected IOException: "
- + e.getMessage());
+ System.out.println("Got expected IOException: " + e.getMessage());
}
}
}
/**
- * Tests the behaviour of HTTPS connection in case of unavailability
- * of requested resource.
+ * Tests the behaviour of HTTPS connection in case of unavailability of requested resource (as
+ * reported by the target web server).
*/
public void testProxyConnection_Not_Found_Response() throws Throwable {
- // setting up the properties pointing to the key/trust stores
+ // set up the properties pointing to the key/trust stores
setUpStoreProperties();
- // create the SSLServerSocket which will be used by server side
- ServerSocket ss = new ServerSocket(0);
+ SSLContext ctx = getContext();
- // create the HostnameVerifier to check that Hostname verification
- // is done
- TestHostnameVerifier hnv = new TestHostnameVerifier();
- HttpsURLConnection.setDefaultHostnameVerifier(hnv);
+ // set the HostnameVerifier required to satisfy SSL - always returns "verified".
+ HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
+
+ // create a server that pretends to be a proxy
+ ProxyConnectDispatcher proxyConnectDispatcher =
+ new ProxyConnectDispatcher(false /* authenticationRequired */);
+ SingleRequestDispatcher notFoundDispatcher =
+ new SingleRequestDispatcher(GET_METHOD, NOT_FOUND_CODE);
+ DelegatingDispatcher delegatingDispatcher =
+ new DelegatingDispatcher(proxyConnectDispatcher, notFoundDispatcher);
+ MockWebServer proxyAndWebServer = createProxyAndWebServer(ctx, delegatingDispatcher);
// create HttpsURLConnection to be tested
- URL url = new URL("https://localhost:" + ss.getLocalPort());
+ URL proxyUrl = proxyAndWebServer.getUrl("/");
+ InetSocketAddress proxyAddress = new InetSocketAddress("localhost", proxyUrl.getPort());
+ URL url = new URL("https://requested.host:55555/requested.data");
HttpsURLConnection connection = (HttpsURLConnection)
- url.openConnection(new Proxy(Proxy.Type.HTTP,
- new InetSocketAddress("localhost",
- ss.getLocalPort())));
+ url.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddress));
connection.setSSLSocketFactory(getContext().getSocketFactory());
try {
- doInteraction(connection, ss, NOT_FOUND_CODE); // NOT FOUND
+ executeClientRequest(connection, false /* doOutput */);
fail("Expected exception was not thrown.");
} catch (FileNotFoundException e) {
if (DO_LOG) {
- System.out.println("Expected exception was thrown: "
- + e.getMessage());
+ System.out.println("Expected exception was thrown: " + e.getMessage());
}
}
}
- /**
- * Log the name of the test case to be executed.
- */
public void setUp() throws Exception {
super.setUp();
if (DO_LOG) {
+ // Log the name of the test case to be executed.
System.out.println();
System.out.println("------------------------");
System.out.println("------ " + getName());
@@ -604,8 +664,8 @@
}
if (store != null) {
- String ksFileName = ("org/apache/harmony/luni/tests/key_store."
- + KeyStore.getDefaultType().toLowerCase());
+ String ksFileName = "org/apache/harmony/luni/tests/key_store." +
+ KeyStore.getDefaultType().toLowerCase();
InputStream in = getClass().getClassLoader().getResourceAsStream(ksFileName);
FileOutputStream out = new FileOutputStream(store);
BufferedInputStream bufIn = new BufferedInputStream(in, 8192);
@@ -627,28 +687,21 @@
}
}
- /**
- * Checks the HttpsURLConnection getter's values and compares
- * them with actual corresponding values of remote peer.
- */
- public static void checkConnectionStateParameters(
- HttpsURLConnection clientConnection, SSLSocket serverPeer)
- throws Exception {
- SSLSession session = serverPeer.getSession();
+ private static void checkConnectionStateParameters(
+ HttpsURLConnection connection, RecordedRequest request) throws Exception {
+ assertEquals(request.getSslCipherSuite(), connection.getCipherSuite());
+ assertEquals(request.getSslLocalPrincipal(), connection.getPeerPrincipal());
+ assertEquals(request.getSslPeerPrincipal(), connection.getLocalPrincipal());
- assertEquals(session.getCipherSuite(), clientConnection.getCipherSuite());
- assertEquals(session.getLocalPrincipal(), clientConnection.getPeerPrincipal());
- assertEquals(session.getPeerPrincipal(), clientConnection.getLocalPrincipal());
-
- Certificate[] serverCertificates = clientConnection.getServerCertificates();
- Certificate[] localCertificates = session.getLocalCertificates();
+ Certificate[] serverCertificates = connection.getServerCertificates();
+ Certificate[] localCertificates = request.getSslLocalCertificates();
assertTrue("Server certificates differ from expected",
- Arrays.equals(serverCertificates, localCertificates));
+ Arrays.equals(serverCertificates, localCertificates));
- localCertificates = clientConnection.getLocalCertificates();
- serverCertificates = session.getPeerCertificates();
+ localCertificates = connection.getLocalCertificates();
+ serverCertificates = request.getSslPeerCertificates();
assertTrue("Local certificates differ from expected",
- Arrays.equals(serverCertificates, localCertificates));
+ Arrays.equals(serverCertificates, localCertificates));
}
/**
@@ -714,81 +767,6 @@
}
/**
- * Performs interaction between client's HttpsURLConnection and
- * servers side (ServerSocket).
- */
- public static Socket doInteraction(final HttpsURLConnection clientConnection,
- final ServerSocket serverSocket)
- throws Throwable {
- return doInteraction(clientConnection, serverSocket, OK_CODE, false);
- }
-
- /**
- * Performs interaction between client's HttpsURLConnection and
- * servers side (ServerSocket). Server will response with specified
- * response code.
- */
- public static Socket doInteraction(final HttpsURLConnection clientConnection,
- final ServerSocket serverSocket,
- final int responseCode)
- throws Throwable {
- return doInteraction(clientConnection, serverSocket, responseCode, false);
- }
-
- /**
- * Performs interaction between client's HttpsURLConnection and
- * servers side (ServerSocket). Server will response with specified
- * response code.
- * @param doAuthentication specifies
- * if the server needs client authentication.
- */
- public static Socket doInteraction(final HttpsURLConnection clientConnection,
- final ServerSocket serverSocket,
- final int responseCode,
- final boolean doAuthentication)
- throws Throwable {
- // set up the connection
- clientConnection.setDoInput(true);
- clientConnection.setConnectTimeout(TIMEOUT);
- clientConnection.setReadTimeout(TIMEOUT);
-
- ServerWork server = new ServerWork(serverSocket, responseCode, doAuthentication);
-
- ClientConnectionWork client = new ClientConnectionWork(clientConnection);
-
- ExecutorService executorService = Executors.newFixedThreadPool(2);
- try {
- Future<Void> serverFuture = executorService.submit(server);
- Future<Void> clientFuture = executorService.submit(client);
-
- Throwable t = null;
- try {
- serverFuture.get(30, TimeUnit.SECONDS);
- } catch (ExecutionException e) {
- t = e.getCause();
- }
- try {
- clientFuture.get(30, TimeUnit.SECONDS);
- } catch (ExecutionException e) {
- // two problems? log the first before overwriting
- if (t != null) {
- t.printStackTrace();
- }
- t = e.getCause();
- }
- if (t != null) {
- throw t;
- }
- } catch (ExecutionException e) {
- throw e.getCause();
- } finally {
- executorService.shutdown();
- }
-
- return server.peerSocket;
- }
-
- /**
* The host name verifier used in test.
*/
static class TestHostnameVerifier implements HostnameVerifier {
@@ -806,303 +784,216 @@
}
/**
- * The base class for mock Client and Server.
+ * Creates a {@link MockWebServer} that acts as both a proxy and then a web server with the
+ * supplied {@link SSLContext} and {@link Dispatcher}. The dispatcher provided must handle the
+ * CONNECT request/responses and {@link SocketPolicy} needed to simulate the hand-off from proxy
+ * to web server. See {@link HttpsURLConnectionTest.ProxyConnectDispatcher}.
*/
- static class Work {
+ private static MockWebServer createProxyAndWebServer(SSLContext ctx, Dispatcher dispatcher)
+ throws IOException {
+ return createServer(ctx, dispatcher, true /* handleProxying */);
+ }
- /**
- * The header of OK HTTP response.
- */
- static final String responseHead = "HTTP/1.1 200 OK\r\n";
+ /**
+ * Creates a {@link MockWebServer} that acts as (only) a web server with the supplied
+ * {@link SSLContext} and {@link Dispatcher}.
+ */
+ private static MockWebServer createWebServer(SSLContext ctx, Dispatcher dispatcher)
+ throws IOException {
+ return createServer(ctx, dispatcher, false /* handleProxying */);
+ }
- /**
- * The response message to be sent to the proxy CONNECT request.
- */
- static final String proxyResponse = responseHead + "\r\n";
+ private static MockWebServer createServer(
+ SSLContext ctx, Dispatcher dispatcher, boolean handleProxying)
+ throws IOException {
+ MockWebServer webServer = new MockWebServer();
+ webServer.useHttps(ctx.getSocketFactory(), handleProxying /* tunnelProxy */);
+ webServer.setDispatcher(dispatcher);
+ webServer.play();
+ return webServer;
+ }
- /**
- * The content of the response to be sent during HTTPS session.
- */
- static final String httpsResponseContent
- = "<HTML>\n"
- + "<HEAD><TITLE>HTTPS Response Content</TITLE></HEAD>\n"
- + "</HTML>";
+ /**
+ * A {@link Dispatcher} that has a list of dispatchers to delegate to, each of which will be
+ * used for one request and then discarded.
+ */
+ private static class DelegatingDispatcher extends Dispatcher {
+ private LinkedList<Dispatcher> delegates = new LinkedList<Dispatcher>();
- /**
- * The tail of the response to be sent during HTTPS session.
- */
- static final String httpsResponseTail
- = "Content-type: text/html\r\n"
- + "Content-length: " + httpsResponseContent.length() + "\r\n"
- + "\r\n"
- + httpsResponseContent;
+ public DelegatingDispatcher(Dispatcher... dispatchers) {
+ addAll(dispatchers);
+ }
- /**
- * The response requiring client's proxy authentication.
- */
- static final String respAuthenticationRequired
- = "HTTP/1.0 407 Proxy authentication required\r\n"
- + "Proxy-authenticate: Basic realm=\"localhost\"\r\n"
- + "\r\n";
+ private void addAll(Dispatcher... dispatchers) {
+ Collections.addAll(delegates, dispatchers);
+ }
- /**
- * The data to be posted by client to the server.
- */
- static final String clientsData = "_.-^ Client's Data ^-._";
+ @Override
+ public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
+ return delegates.removeFirst().dispatch(request);
+ }
- /**
- * The print stream used for debug log.
- * If it is null debug info will not be printed.
- */
- private PrintStream out = System.out;
+ @Override
+ public SocketPolicy peekSocketPolicy() {
+ return delegates.getFirst().peekSocketPolicy();
+ }
+ }
- /**
- * Prints log message.
- */
- public synchronized void log(String message) {
- if (DO_LOG && (out != null)) {
- out.println("[" + this + "]: " + message);
- }
+ /** Handles a request for SSL tunnel: Answers with a request to authenticate. */
+ private static class ProxyConnectAuthFailDispatcher extends Dispatcher {
+
+ @Override
+ public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
+ assertEquals("CONNECT", request.getMethod());
+
+ MockResponse response = new MockResponse();
+ response.setResponseCode(AUTHENTICATION_REQUIRED_CODE);
+ response.addHeader("Proxy-authenticate: Basic realm=\"localhost\"");
+ log("Authentication required. Sending response: " + response);
+ return response;
+ }
+
+ private void log(String msg) {
+ HttpsURLConnectionTest.log("ProxyConnectAuthFailDispatcher", msg);
}
}
/**
- * The class used for server side works.
+ * Handles a request for SSL tunnel: Answers with a success and the socket is upgraded to SSL.
*/
- static class ServerWork extends Work implements Callable<Void> {
+ private static class ProxyConnectDispatcher extends Dispatcher {
- // the server socket used for connection
- private final ServerSocket serverSocket;
+ private final boolean authenticationRequired;
- // indicates if the server acts as proxy server
- private final boolean actAsProxy;
+ private ProxyConnectDispatcher(boolean authenticationRequired) {
+ this.authenticationRequired = authenticationRequired;
+ }
- // indicates if the server needs proxy authentication
- private final boolean needProxyAuthentication;
+ @Override
+ public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
+ if (authenticationRequired) {
+ // check provided authorization credentials
+ assertNotNull("no proxy-authorization credentials: " + request,
+ request.getHeader("proxy-authorization"));
+ log("Got authenticated request:\n" + request);
+ log("------------------");
+ }
- // response code to be send to the client peer
+ assertEquals("CONNECT", request.getMethod());
+ log("Send proxy response");
+ MockResponse response = new MockResponse();
+ response.setResponseCode(200);
+ response.setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END);
+ return response;
+ }
+
+ @Override
+ public SocketPolicy peekSocketPolicy() {
+ return SocketPolicy.UPGRADE_TO_SSL_AT_END;
+ }
+
+ private void log(String msg) {
+ HttpsURLConnectionTest.log("ProxyConnectDispatcher", msg);
+ }
+ }
+
+ /**
+ * Handles a request: Answers with a response with a specified status code.
+ * If the {@code expectedMethod} is {@code POST} a hardcoded response body {@link #POST_DATA}
+ * will be included in the response.
+ */
+ private static class SingleRequestDispatcher extends Dispatcher {
+
+ private final String expectedMethod;
private final int responseCode;
- // the socket connected with client peer
- private Socket peerSocket;
+ private RecordedRequest lastRequest;
- /**
- * Creates the thread acting as a server side.
- * @param serverSocket the server socket to be used during connection
- * @param responseCode the response code to be sent to the client
- * @param needProxyAuthentication
- * indicates if the server needs proxy authentication
- */
- public ServerWork(ServerSocket serverSocket,
- int responseCode,
- boolean needProxyAuthentication) {
- this.serverSocket = serverSocket;
+ private SingleRequestDispatcher(String expectedMethod, int responseCode) {
this.responseCode = responseCode;
- this.needProxyAuthentication = needProxyAuthentication;
- // will act as a proxy server if the specified server socket
- // is not a secure server socket
- this.actAsProxy = !(serverSocket instanceof SSLServerSocket);
- if (!actAsProxy) {
- // demand client to send its certificate
- ((SSLServerSocket) serverSocket).setNeedClientAuth(true);
- }
+ this.expectedMethod = expectedMethod;
}
- /**
- * Closes the connection.
- */
- public void closeSocket(Socket socket) {
- if (socket == null) {
- return;
+ @Override
+ public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
+ if (lastRequest != null) {
+ fail("More than one request received");
}
- try {
- socket.getInputStream().close();
- } catch (IOException e) {}
- try {
- socket.getOutputStream().close();
- } catch (IOException e) {}
- try {
- socket.close();
- } catch (IOException e) {}
+ log("Request received: " + request);
+ lastRequest = request;
+ assertEquals(expectedMethod, request.getMethod());
+ if (POST_METHOD.equals(expectedMethod)) {
+ assertEquals(POST_DATA, request.getUtf8Body());
+ }
+
+ MockResponse response = new MockResponse();
+ response.setResponseCode(responseCode);
+ response.setBody(RESPONSE_CONTENT);
+
+ log("Responding with: " + response);
+ return response;
}
- /**
- * Performs the actual server work.
- * If some exception occurs during the work it will be
- * stored in the <code>thrown</code> field.
- */
- public Void call() throws Exception {
- // the buffer used for reading the messages
- byte[] buff = new byte[2048];
- // the number of bytes read into the buffer
- try {
- // configure the server socket to avoid blocking
- serverSocket.setSoTimeout(TIMEOUT);
- // accept client connection
- peerSocket = serverSocket.accept();
- // configure the client connection to avoid blocking
- peerSocket.setSoTimeout(TIMEOUT);
- log("Client connection ACCEPTED");
-
- InputStream is = peerSocket.getInputStream();
- OutputStream os = peerSocket.getOutputStream();
-
- int num = is.read(buff);
- if (num == -1) {
- log("Unexpected EOF");
- return null;
- }
-
- String message = new String(buff, 0, num);
- log("Got request:\n" + message);
- log("------------------");
-
- if (!actAsProxy) {
- // Act as Server (not Proxy) side
- if (message.startsWith("POST")) {
- // client connection sent some data
- log("try to read client data");
- String data = message.substring(message.indexOf("\r\n\r\n")+4);
- log("client's data: '" + data + "'");
- // check the received data
- assertEquals(clientsData, data);
- }
- } else {
- if (needProxyAuthentication) {
- // Do proxy work
- log("Authentication required...");
- // send Authentication Request
- os.write(respAuthenticationRequired.getBytes());
- // read request
- num = is.read(buff);
- if (num == -1) {
- // this connection was closed,
- // do clean up and create new one:
- closeSocket(peerSocket);
- peerSocket = serverSocket.accept();
- peerSocket.setSoTimeout(TIMEOUT);
- log("New client connection ACCEPTED");
- is = peerSocket.getInputStream();
- os = peerSocket.getOutputStream();
- num = is.read(buff);
- }
- message = new String(buff, 0, num);
- log("Got authenticated request:\n" + message);
- log("------------------");
- // check provided authorization credentials
- assertTrue("no proxy-authorization credentials: " + message,
- message.toLowerCase().indexOf("proxy-authorization:") != -1);
- }
-
- assertTrue(message.startsWith("CONNECT"));
- // request for SSL tunnel
- log("Send proxy response");
- os.write(proxyResponse.getBytes());
-
- log("Perform SSL Handshake...");
- // create sslSocket acting as a remote server peer
- SSLSocket sslSocket = (SSLSocket)
- getContext().getSocketFactory().createSocket(peerSocket,
- "localhost",
- peerSocket.getPort(),
- true); // do autoclose
- sslSocket.setUseClientMode(false);
- // demand client authentication
- sslSocket.setNeedClientAuth(true);
- sslSocket.startHandshake();
- peerSocket = sslSocket;
- is = peerSocket.getInputStream();
- os = peerSocket.getOutputStream();
-
- // read the HTTP request sent by secure connection
- // (HTTPS request)
- num = is.read(buff);
- message = new String(buff, 0, num);
- log("[Remote Server] Request from SSL tunnel:\n" + message);
- log("------------------");
-
- if (message.startsWith("POST")) {
- // client connection sent some data
- log("[Remote Server] try to read client data");
- String data = message.substring(message.indexOf("\r\n\r\n")+4);
- log("[Remote Server] client's data: '" + message + "'");
- // check the received data
- assertEquals(clientsData, data);
- }
-
- log("[Remote Server] Sending the response by SSL tunnel...");
- }
-
- // send the response with specified response code
- os.write(("HTTP/1.1 " + responseCode
- + " Message\r\n" + httpsResponseTail).getBytes());
- os.flush();
- os.close();
- log("Work is DONE actAsProxy=" + actAsProxy);
- return null;
- } finally {
- closeSocket(peerSocket);
- try {
- serverSocket.close();
- } catch (IOException e) {}
- }
+ public RecordedRequest getLastRequest() {
+ return lastRequest;
}
- @Override public String toString() {
- return actAsProxy ? "Proxy Server" : "Server";
+ @Override
+ public SocketPolicy peekSocketPolicy() {
+ return SocketPolicy.DISCONNECT_AT_END;
+ }
+
+ private void log(String msg) {
+ HttpsURLConnectionTest.log("SingleRequestDispatcher", msg);
}
}
/**
- * The class used for client side work.
+ * Executes an HTTP request using the supplied connection. If {@code doOutput} is {@code true}
+ * the request made is a POST and the request body sent is {@link #POST_DATA}.
+ * If {@code doOutput} is {@code false} the request made is a GET. The response must be a
+ * success with a body {@link #RESPONSE_CONTENT}.
*/
- static class ClientConnectionWork extends Work implements Callable<Void> {
+ private static void executeClientRequest(
+ HttpsURLConnection connection, boolean doOutput) throws IOException {
- // connection to be used to contact the server side
- private HttpsURLConnection connection;
+ // set up the connection
+ connection.setDoInput(true);
+ connection.setConnectTimeout(TIMEOUT);
+ connection.setReadTimeout(TIMEOUT);
+ connection.setDoOutput(doOutput);
- /**
- * Creates the thread acting as a client side.
- * @param connection connection to be used to contact the server side
- */
- public ClientConnectionWork(HttpsURLConnection connection) {
- this.connection = connection;
- log("Created over connection: " + connection.getClass());
+ log("Client", "Opening the connection to " + connection.getURL());
+ connection.connect();
+ log("Client", "Connection has been ESTABLISHED, using proxy: " + connection.usingProxy());
+ if (doOutput) {
+ log("Client", "Posting data");
+ // connection configured to post data, do so
+ OutputStream os = connection.getOutputStream();
+ os.write(POST_DATA.getBytes());
}
-
- /**
- * Performs the actual client work.
- * If some exception occurs during the work it will be
- * stored in the <code>thrown<code> field.
- */
- public Void call() throws Exception {
- log("Opening the connection to " + connection.getURL());
- connection.connect();
- log("Connection has been ESTABLISHED, using proxy: " + connection.usingProxy());
- if (connection.getDoOutput()) {
- log("Posting data");
- // connection configured to post data, do so
- connection.getOutputStream().write(clientsData.getBytes());
- }
- // read the content of HTTP(s) response
- InputStream is = connection.getInputStream();
- log("Input Stream obtained");
- byte[] buff = new byte[2048];
- int num = 0;
- int byt = 0;
- while ((num < buff.length) && ((byt = is.read()) != -1)) {
- buff[num++] = (byte) byt;
- }
- String message = new String(buff, 0, num);
- log("Got content:\n" + message);
- log("------------------");
- log("Response code: " + connection.getResponseCode());
- assertEquals(httpsResponseContent, message);
- return null;
+ // read the content of HTTP(s) response
+ InputStream is = connection.getInputStream();
+ log("Client", "Input Stream obtained");
+ byte[] buff = new byte[2048];
+ int num = 0;
+ int byt;
+ while ((num < buff.length) && ((byt = is.read()) != -1)) {
+ buff[num++] = (byte) byt;
}
+ String message = new String(buff, 0, num);
+ log("Client", "Got content:\n" + message);
+ log("Client", "------------------");
+ log("Client", "Response code: " + connection.getResponseCode());
+ assertEquals(RESPONSE_CONTENT, message);
+ }
- @Override public String toString() {
- return "Client Connection";
+ /**
+ * Prints log message.
+ */
+ public static synchronized void log(String origin, String message) {
+ if (DO_LOG) {
+ System.out.println("[" + origin + "]: " + message);
}
}
}
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/io/ObjectInputStreamTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/io/ObjectInputStreamTest.java
index 5b89172..d1f92ec 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/io/ObjectInputStreamTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/io/ObjectInputStreamTest.java
@@ -1001,8 +1001,7 @@
// Regression Test for JIRA 2192
public void test_readObject_withPrimitiveClass() throws Exception {
- File file = new File("test.ser");
- file.deleteOnExit();
+ File file = File.createTempFile("ObjectInputStreamTest", ".ser");
Test test = new Test();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
file));
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
index d1674f3..e2a3a47 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
@@ -209,10 +209,14 @@
URL url2;
+ URL url3;
+
URLConnection uc;
URLConnection uc2;
+ URLConnection uc3;
+
Support_TestWebServer server;
@Override
@@ -225,6 +229,8 @@
uc = url.openConnection();
url2 = new URL("http://localhost:" + port + "/test2");
uc2 = url2.openConnection();
+ url3 = new URL("http://localhost:" + port + "/test3");
+ uc3 = url3.openConnection();
fileURL = createTempHelloWorldFile();
fileURLCon = fileURL.openConnection();
@@ -239,6 +245,7 @@
server.close();
((HttpURLConnection) uc).disconnect();
((HttpURLConnection) uc2).disconnect();
+ ((HttpURLConnection) uc3).disconnect();
}
/**
@@ -472,8 +479,22 @@
assertEquals(Support_TestWebData.test1.length, uc.getContentLength());
assertEquals(Support_TestWebData.test2.length, uc2.getContentLength());
- assertNotNull(jarURLCon.getContentLength());
- assertNotNull(gifURLCon.getContentLength());
+ assertTrue(jarURLCon.getContentLength() > 0);
+ assertTrue(gifURLCon.getContentLength() > 0);
+
+ fileURLCon.getInputStream().close();
+ }
+
+ /**
+ * {@link java.net.URLConnection#getContentLengthLong()}
+ */
+ public void test_getContentLengthLong() throws Exception {
+ assertEquals(testString.getBytes().length, fileURLCon.getContentLengthLong());
+ assertEquals(Support_TestWebData.test1.length, uc.getContentLengthLong());
+ assertEquals(Support_TestWebData.test2.length, uc2.getContentLengthLong());
+
+ assertTrue(jarURLCon.getContentLength() > 0);
+ assertTrue(gifURLCon.getContentLength() > 0);
fileURLCon.getInputStream().close();
}
@@ -719,17 +740,17 @@
}
/**
- * @throws IOException
* {@link java.net.URLConnection#getHeaderFieldInt(String, int)}
*/
public void test_getHeaderFieldInt() throws IOException, ParseException {
- Support_TestWebData params = Support_TestWebData.testParams[1];
+ // Test getHeaderFieldInt() can read an int value.
+ Support_TestWebData params1 = Support_TestWebData.testParams[1];
+ int hf = uc2.getHeaderFieldInt("Content-Length", Integer.MIN_VALUE);
+ assertEquals(params1.testLength, hf);
- int hf = 0;
+ // The remaining fields should be invalid or missing. Confirm the default is returned.
hf = uc2.getHeaderFieldInt("Content-Encoding", Integer.MIN_VALUE);
assertEquals(Integer.MIN_VALUE, hf);
- hf = uc2.getHeaderFieldInt("Content-Length", Integer.MIN_VALUE);
- assertEquals(params.testLength, hf);
hf = uc2.getHeaderFieldInt("Content-Type", Integer.MIN_VALUE);
assertEquals(Integer.MIN_VALUE, hf);
hf = uc2.getHeaderFieldInt("Date", Integer.MIN_VALUE);
@@ -745,6 +766,43 @@
hf = uc2.getHeaderFieldInt("DoesNotExist", Integer.MIN_VALUE);
assertEquals(Integer.MIN_VALUE, hf);
+ // Test getHeaderFieldInt() for a value outside of the range of int.
+ Support_TestWebData params2 = Support_TestWebData.testParams[2];
+ hf = uc3.getHeaderFieldInt("Content-Length", Integer.MIN_VALUE);
+ assertEquals(Integer.MIN_VALUE, hf);
+ }
+
+ /**
+ * {@link java.net.URLConnection#getHeaderFieldLong(String, long)}
+ */
+ public void test_getHeaderFieldLong() throws IOException, ParseException {
+ // Test getHeaderFieldLong() can read an int value.
+ Support_TestWebData params0 = Support_TestWebData.testParams[0];
+ long hf = uc.getHeaderFieldLong("Content-Length", Long.MIN_VALUE);
+ assertEquals(params0.testLength, hf);
+
+ // Test getHeaderFieldLong() for a value outside of the range of int.
+ Support_TestWebData params2 = Support_TestWebData.testParams[2];
+ hf = uc3.getHeaderFieldLong("Content-Length", Long.MIN_VALUE);
+ assertEquals(params2.testLength, hf);
+
+ // The remaining fields should be invalid or missing. Confirm the default is returned.
+ hf = uc3.getHeaderFieldLong("Content-Encoding", Long.MIN_VALUE);
+ assertEquals(Long.MIN_VALUE, hf);
+ hf = uc3.getHeaderFieldInt("Content-Type", Integer.MIN_VALUE);
+ assertEquals(Integer.MIN_VALUE, hf);
+ hf = uc3.getHeaderFieldInt("Date", Integer.MIN_VALUE);
+ assertEquals(Integer.MIN_VALUE, hf);
+ hf = uc3.getHeaderFieldInt("Expires", Integer.MIN_VALUE);
+ assertEquals(Integer.MIN_VALUE, hf);
+ hf = uc3.getHeaderFieldInt("SERVER", Integer.MIN_VALUE);
+ assertEquals(Integer.MIN_VALUE, hf);
+ hf = uc3.getHeaderFieldInt("Last-Modified", Integer.MIN_VALUE);
+ assertEquals(Integer.MIN_VALUE, hf);
+ hf = uc3.getHeaderFieldInt("accept-ranges", Integer.MIN_VALUE);
+ assertEquals(Integer.MIN_VALUE, hf);
+ hf = uc3.getHeaderFieldInt("DoesNotExist", Integer.MIN_VALUE);
+ assertEquals(Integer.MIN_VALUE, hf);
}
/**
@@ -1253,7 +1311,7 @@
String cts = System.getProperty("java.io.tmpdir");
File tmpDir = new File(cts);
Support_Resources.copyFile(tmpDir, null, "Harmony.GIF");
- URL fUrl1 = new URL("file:/" + tmpDir.getPath()
+ URL fUrl1 = new URL("file://" + tmpDir.getPath()
+ "/Harmony.GIF");
URLConnection con1 = fUrl1.openConnection();
return con1;
diff --git a/luni/src/test/java/tests/java/sql/SqlDateTest.java b/luni/src/test/java/tests/java/sql/SqlDateTest.java
new file mode 100644
index 0000000..fb226af
--- /dev/null
+++ b/luni/src/test/java/tests/java/sql/SqlDateTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+import java.sql.Date;
+
+import junit.framework.TestCase;
+
+public class SqlDateTest extends TestCase {
+
+ public void testValueOf() {
+ String[] dates = {
+ "2001-12-31", "2001-12-1", "2001-1-1", "1900-12-31"
+ };
+
+ for (String date : dates) {
+ Date.valueOf(date);
+ }
+ }
+
+ public void testValueOfInvalidDate() {
+ String[] invalidDates = {
+ "",
+ "+2001-12-31", "2001-+12-31", "2001-12-+31",
+ "-2001-12-31", "2001--12-31", "2001-12--31",
+ "2001--","2001--31","-12-31", "-12-", "--31",
+ "2000000001-12-31"
+ };
+
+ for (String date : invalidDates) {
+ try {
+ Date.valueOf(date);
+ fail();
+ } catch (IllegalArgumentException expected) { }
+ }
+ }
+
+}
diff --git a/luni/src/test/resources/tests/resources/java/util/zip/EmptyArchive.zip b/luni/src/test/resources/tests/resources/java/util/zip/EmptyArchive.zip
new file mode 100644
index 0000000..15cb0ec
--- /dev/null
+++ b/luni/src/test/resources/tests/resources/java/util/zip/EmptyArchive.zip
Binary files differ
diff --git a/support/src/test/java/libcore/java/lang/ref/FinalizationTester.java b/support/src/test/java/libcore/java/lang/ref/FinalizationTester.java
index 66ac1a4..d758317 100644
--- a/support/src/test/java/libcore/java/lang/ref/FinalizationTester.java
+++ b/support/src/test/java/libcore/java/lang/ref/FinalizationTester.java
@@ -26,7 +26,9 @@
private FinalizationTester() {}
public static void induceFinalization() {
- System.gc();
+ // System.gc() does not garbage collect every time. Runtime.gc() is
+ // more likely to perfom a gc.
+ Runtime.getRuntime().gc();
enqueueReferences();
System.runFinalization();
}
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index b33aefe..da6fcd6 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -441,22 +441,34 @@
// Needed by our OpenSSL provider
provide("Cipher", "AES/CBC/NOPADDING");
provide("Cipher", "AES/CBC/PKCS5PADDING");
+ provide("Cipher", "AES/CBC/PKCS7PADDING");
provide("Cipher", "AES/CFB/NOPADDING");
provide("Cipher", "AES/CFB/PKCS5PADDING");
+ provide("Cipher", "AES/CFB/PKCS7PADDING");
provide("Cipher", "AES/CTR/NOPADDING");
provide("Cipher", "AES/CTR/PKCS5PADDING");
+ provide("Cipher", "AES/CTR/PKCS7PADDING");
provide("Cipher", "AES/ECB/NOPADDING");
provide("Cipher", "AES/ECB/PKCS5PADDING");
+ provide("Cipher", "AES/ECB/PKCS7PADDING");
provide("Cipher", "AES/OFB/NOPADDING");
provide("Cipher", "AES/OFB/PKCS5PADDING");
+ provide("Cipher", "AES/OFB/PKCS7PADDING");
provide("Cipher", "DESEDE/CBC/NOPADDING");
provide("Cipher", "DESEDE/CBC/PKCS5PADDING");
+ provide("Cipher", "DESEDE/CBC/PKCS7PADDING");
provide("Cipher", "DESEDE/CFB/NOPADDING");
provide("Cipher", "DESEDE/CFB/PKCS5PADDING");
+ provide("Cipher", "DESEDE/CFB/PKCS7PADDING");
provide("Cipher", "DESEDE/ECB/NOPADDING");
provide("Cipher", "DESEDE/ECB/PKCS5PADDING");
+ provide("Cipher", "DESEDE/ECB/PKCS7PADDING");
provide("Cipher", "DESEDE/OFB/NOPADDING");
provide("Cipher", "DESEDE/OFB/PKCS5PADDING");
+ provide("Cipher", "DESEDE/OFB/PKCS7PADDING");
+
+ // Provided by our OpenSSL provider
+ provideCipherPaddings("AES", new String[] { "PKCS7Padding" });
// removed LDAP
unprovide("CertStore", "LDAP");
@@ -507,6 +519,12 @@
if (Security.getProvider("AndroidKeyStore") != null) {
provide("KeyStore", "AndroidKeyStore");
}
+
+ // TimaKeyStore provider
+ if (Security.getProvider("TimaKeyStore") != null) {
+ provide("KeyStore", "TimaKeyStore");
+ }
+
}
}
@@ -525,12 +543,18 @@
public static final Set<String> KEY_TYPES = new HashSet<String>(Arrays.asList(
"RSA",
"DSA",
- // DH_* are specified by standard names, but do not seem to be supported by RI
- // "DH_RSA",
- // "DH_DSA",
+ "DH_RSA",
+ "DH_DSA",
"EC",
"EC_EC",
"EC_RSA"));
+ static {
+ if (IS_RI) {
+ // DH_* are specified by standard names, but do not seem to be supported by RI
+ KEY_TYPES.remove("DH_RSA");
+ KEY_TYPES.remove("DH_DSA");
+ }
+ }
public static final Set<String> SSL_SOCKET_PROTOCOLS = new HashSet<String>(Arrays.asList(
// "SSLv2",
@@ -566,12 +590,8 @@
public static final Set<String> SSL_SOCKET_PROTOCOLS_DEFAULT_SSLENGINE =
new HashSet<String>(SSL_SOCKET_PROTOCOLS_CLIENT_DEFAULT);
static {
- // No TLSv1.1 or TLSv1.2 support on SSLEngine based provider
- if (!IS_RI) {
- SSL_SOCKET_PROTOCOLS_SSLENGINE.remove("TLSv1.1");
- SSL_SOCKET_PROTOCOLS_SSLENGINE.remove("TLSv1.2");
- SSL_SOCKET_PROTOCOLS_DEFAULT_SSLENGINE.remove("TLSv1.1");
- SSL_SOCKET_PROTOCOLS_DEFAULT_SSLENGINE.remove("TLSv1.2");
+ if (IS_RI) {
+ SSL_SOCKET_PROTOCOLS_DEFAULT_SSLENGINE.add("SSLv2Hello");
}
}
@@ -639,13 +659,13 @@
static {
// NOTE: This list needs to be kept in sync with Javadoc of javax.net.ssl.SSLSocket and
// javax.net.ssl.SSLEngine.
- addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA");
- addOpenSsl("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
- addOpenSsl("TLS_RSA_WITH_AES_256_CBC_SHA");
- addOpenSsl("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA");
- addOpenSsl("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA");
- addOpenSsl("TLS_DHE_RSA_WITH_AES_256_CBC_SHA");
- addOpenSsl("TLS_DHE_DSS_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_RSA_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_DHE_RSA_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_DHE_DSS_WITH_AES_256_CBC_SHA");
addBoth( "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA");
addBoth( "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
addBoth( "TLS_RSA_WITH_AES_128_CBC_SHA");
@@ -670,35 +690,35 @@
// TLSv1.2 cipher suites
addBoth( "TLS_RSA_WITH_NULL_SHA256");
addBoth( "TLS_RSA_WITH_AES_128_CBC_SHA256");
- addOpenSsl("TLS_RSA_WITH_AES_256_CBC_SHA256");
+ addBoth( "TLS_RSA_WITH_AES_256_CBC_SHA256");
addOpenSsl("TLS_RSA_WITH_AES_128_GCM_SHA256");
addOpenSsl("TLS_RSA_WITH_AES_256_GCM_SHA384");
addBoth( "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256");
- addOpenSsl("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256");
+ addBoth( "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256");
addOpenSsl("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256");
addOpenSsl("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384");
addBoth( "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256");
- addOpenSsl("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256");
+ addBoth( "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256");
addOpenSsl("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256");
addOpenSsl("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384");
addBoth( "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256");
- addOpenSsl("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384");
+ addBoth( "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384");
addOpenSsl("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256");
addOpenSsl("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384");
addBoth( "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256");
- addOpenSsl("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384");
+ addBoth( "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384");
addOpenSsl("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256");
addOpenSsl("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384");
addBoth( "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256");
- addOpenSsl("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");
+ addBoth( "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");
addOpenSsl("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
addOpenSsl("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
addBoth( "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256");
- addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
+ addBoth( "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
addBoth( "TLS_DH_anon_WITH_AES_128_CBC_SHA256");
- addOpenSsl("TLS_DH_anon_WITH_AES_256_CBC_SHA256");
+ addBoth( "TLS_DH_anon_WITH_AES_256_CBC_SHA256");
addOpenSsl("TLS_DH_anon_WITH_AES_128_GCM_SHA256");
addOpenSsl("TLS_DH_anon_WITH_AES_256_GCM_SHA384");
@@ -706,8 +726,8 @@
addBoth(CIPHER_SUITE_SECURE_RENEGOTIATION);
// non-defaultCipherSuites
- addOpenSsl("TLS_ECDH_anon_WITH_AES_256_CBC_SHA");
- addOpenSsl("TLS_DH_anon_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_ECDH_anon_WITH_AES_256_CBC_SHA");
+ addBoth( "TLS_DH_anon_WITH_AES_256_CBC_SHA");
addBoth( "TLS_ECDH_anon_WITH_AES_128_CBC_SHA");
addBoth( "TLS_DH_anon_WITH_AES_128_CBC_SHA");
addBoth( "TLS_ECDH_anon_WITH_RC4_128_SHA");
@@ -760,30 +780,68 @@
CIPHER_SUITES = (IS_RI) ? CIPHER_SUITES_RI : CIPHER_SUITES_OPENSSL;
}
+ /**
+ * Cipher suites that are not negotiated when TLSv1.2 is selected on the RI.
+ */
+ public static final List<String> CIPHER_SUITES_OBSOLETE_TLS12 =
+ Arrays.asList(
+ "SSL_RSA_WITH_DES_CBC_SHA",
+ "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+ "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+ "SSL_DH_anon_WITH_DES_CBC_SHA",
+ "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+ "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"
+ );
+
// NOTE: This list needs to be kept in sync with Javadoc of javax.net.ssl.SSLSocket and
// javax.net.ssl.SSLEngine.
public static final List<String> CIPHER_SUITES_DEFAULT = (IS_RI)
- ? Arrays.asList("TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
- "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_WITH_RC4_128_SHA",
- "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
- "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
- "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDH_RSA_WITH_RC4_128_SHA",
- "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
- "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
+ ? Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+ "TLS_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_WITH_RC4_128_MD5",
+ "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
+ "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
+ "SSL_RSA_WITH_RC4_128_SHA",
+ "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
+ "TLS_ECDH_RSA_WITH_RC4_128_SHA",
+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_WITH_3DES_EDE_CBC_SHA")
+ "SSL_RSA_WITH_RC4_128_MD5",
+ "TLS_EMPTY_RENEGOTIATION_INFO_SCSV")
: Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
@@ -829,28 +887,6 @@
public static final List<String> CIPHER_SUITES_DEFAULT_SSLENGINE =
new ArrayList<String>(CIPHER_SUITES_DEFAULT);
public static final Set<String> CIPHER_SUITES_SSLENGINE = new HashSet<String>(CIPHER_SUITES);
- static {
- // No Elliptic Curve or TLSv1.2 cipher suite support on SSLEngine based provider
- if (!IS_RI) {
- Iterator<String> i = CIPHER_SUITES_SSLENGINE.iterator();
- while (i.hasNext()) {
- String cs = i.next();
- if (cs.startsWith("TLS_EC") || cs.contains("_SHA256") || cs.contains("_SHA384")
- || cs.equals(CIPHER_SUITE_SECURE_RENEGOTIATION)) {
- i.remove();
- }
- }
-
- i = CIPHER_SUITES_DEFAULT_SSLENGINE.iterator();
- while (i.hasNext()) {
- String cs = i.next();
- if (cs.startsWith("TLS_EC") || cs.contains("_SHA256") || cs.contains("_SHA384")
- || cs.equals(CIPHER_SUITE_SECURE_RENEGOTIATION)) {
- i.remove();
- }
- }
- }
- }
public static final Map<String, Class<? extends KeySpec>> PRIVATE_KEY_SPEC_CLASSES;
public static final Map<String, Class<? extends KeySpec>> PUBLIC_KEY_SPEC_CLASSES;
diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java
index 8f905c3..86d6f4c 100644
--- a/support/src/test/java/libcore/java/security/TestKeyStore.java
+++ b/support/src/test/java/libcore/java/security/TestKeyStore.java
@@ -102,7 +102,6 @@
public final char[] keyPassword;
public final KeyManager[] keyManagers;
public final TrustManager[] trustManagers;
- public final TestKeyManager keyManager;
public final TestTrustManager trustManager;
private TestKeyStore(KeyStore keyStore, char[] storePassword, char[] keyPassword) {
@@ -111,7 +110,6 @@
this.keyPassword = keyPassword;
this.keyManagers = createKeyManagers(keyStore, storePassword);
this.trustManagers = createTrustManagers(keyStore);
- this.keyManager = (TestKeyManager)keyManagers[0];
this.trustManager = (TestTrustManager)trustManagers[0];
}
@@ -156,11 +154,16 @@
.signer(ROOT_CA.getPrivateKey("RSA", "RSA"))
.rootCa(ROOT_CA.getRootCertificate("RSA"))
.build();
- SERVER = new Builder()
- .aliasPrefix("server")
- .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
- .rootCa(INTERMEDIATE_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);
+ }
CLIENT = new TestKeyStore(createClient(INTERMEDIATE_CA.keyStore), null, null);
CLIENT_CERTIFICATE = new Builder()
.aliasPrefix("client")
diff --git a/support/src/test/java/libcore/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java b/support/src/test/java/libcore/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java
index fd5cc0b..ce40129 100644
--- a/support/src/test/java/libcore/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java
+++ b/support/src/test/java/libcore/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java
@@ -21,6 +21,7 @@
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
+import java.security.interfaces.ECPrivateKey;
import java.security.spec.DSAParameterSpec;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.RSAPrivateKeySpec;
@@ -71,6 +72,10 @@
keyPairGenerator.initialize(new DSAParameterSpec(
originalKeySpec.getP(), originalKeySpec.getQ(), originalKeySpec.getG()));
result = keyPairGenerator.generateKeyPair().getPrivate();
+ } else if ("EC".equals(keyAlgorithm)) {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
+ keyPairGenerator.initialize(((ECPrivateKey) originalPrivateKey).getParams());
+ result = keyPairGenerator.generateKeyPair().getPrivate();
} else {
Assert.fail("Unsupported key algorithm: " + originalPrivateKey.getAlgorithm());
result = null;
diff --git a/support/src/test/java/libcore/javax/net/ssl/TestSSLContext.java b/support/src/test/java/libcore/javax/net/ssl/TestSSLContext.java
index b2386dd..9793d9a 100644
--- a/support/src/test/java/libcore/javax/net/ssl/TestSSLContext.java
+++ b/support/src/test/java/libcore/javax/net/ssl/TestSSLContext.java
@@ -33,7 +33,7 @@
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import junit.framework.Assert;
import libcore.java.security.StandardNames;
@@ -81,10 +81,10 @@
public final char[] clientStorePassword;
public final KeyStore serverKeyStore;
public final char[] serverStorePassword;
- public final X509ExtendedKeyManager clientKeyManager;
- public final X509ExtendedKeyManager serverKeyManager;
- public final X509TrustManager clientTrustManager;
- public final X509TrustManager serverTrustManager;
+ public final KeyManager[] clientKeyManagers;
+ public final KeyManager[] serverKeyManagers;
+ public final X509ExtendedTrustManager clientTrustManager;
+ public final X509ExtendedTrustManager serverTrustManager;
public final SSLContext clientContext;
public final SSLContext serverContext;
public final SSLServerSocket serverSocket;
@@ -95,10 +95,10 @@
char[] clientStorePassword,
KeyStore serverKeyStore,
char[] serverStorePassword,
- X509ExtendedKeyManager clientKeyManager,
- X509ExtendedKeyManager serverKeyManager,
- X509TrustManager clientTrustManager,
- X509TrustManager serverTrustManager,
+ KeyManager[] clientKeyManagers,
+ KeyManager[] serverKeyManagers,
+ X509ExtendedTrustManager clientTrustManager,
+ X509ExtendedTrustManager serverTrustManager,
SSLContext clientContext,
SSLContext serverContext,
SSLServerSocket serverSocket,
@@ -108,8 +108,8 @@
this.clientStorePassword = clientStorePassword;
this.serverKeyStore = serverKeyStore;
this.serverStorePassword = serverStorePassword;
- this.clientKeyManager = clientKeyManager;
- this.serverKeyManager = serverKeyManager;
+ this.clientKeyManagers = clientKeyManagers;
+ this.serverKeyManagers = serverKeyManagers;
this.clientTrustManager = clientTrustManager;
this.serverTrustManager = serverTrustManager;
this.clientContext = clientContext;
@@ -141,15 +141,15 @@
* TestSSLContext creation method that allows separate creation of server key store
*/
public static TestSSLContext create(TestKeyStore client, TestKeyStore server) {
- String protocol = "TLS";
+ String protocol = "TLSv1.2";
SSLContext clientContext =
createSSLContext(protocol, client.keyManagers, client.trustManagers);
SSLContext serverContext =
createSSLContext(protocol, server.keyManagers, server.trustManagers);
return create(client.keyStore, client.storePassword,
server.keyStore, server.storePassword,
- client.keyManagers[0],
- server.keyManagers[0],
+ client.keyManagers,
+ server.keyManagers,
client.trustManagers[0],
server.trustManagers[0],
clientContext,
@@ -161,8 +161,8 @@
*/
public static TestSSLContext create(KeyStore clientKeyStore, char[] clientStorePassword,
KeyStore serverKeyStore, char[] serverStorePassword,
- KeyManager clientKeyManagers,
- KeyManager serverKeyManagers,
+ KeyManager[] clientKeyManagers,
+ KeyManager[] serverKeyManagers,
TrustManager clientTrustManagers,
TrustManager serverTrustManagers,
SSLContext clientContext,
@@ -175,10 +175,10 @@
return new TestSSLContext(clientKeyStore, clientStorePassword,
serverKeyStore, serverStorePassword,
- (X509ExtendedKeyManager) clientKeyManagers,
- (X509ExtendedKeyManager) serverKeyManagers,
- (X509TrustManager) clientTrustManagers,
- (X509TrustManager) serverTrustManagers,
+ clientKeyManagers,
+ serverKeyManagers,
+ (X509ExtendedTrustManager) clientTrustManagers,
+ (X509ExtendedTrustManager) serverTrustManagers,
clientContext, serverContext,
serverSocket, host, port);
} catch (RuntimeException e) {
diff --git a/support/src/test/java/libcore/javax/net/ssl/TestSSLEnginePair.java b/support/src/test/java/libcore/javax/net/ssl/TestSSLEnginePair.java
index b6efdeb..709f568 100644
--- a/support/src/test/java/libcore/javax/net/ssl/TestSSLEnginePair.java
+++ b/support/src/test/java/libcore/javax/net/ssl/TestSSLEnginePair.java
@@ -47,10 +47,19 @@
}
public static TestSSLEnginePair create(TestSSLContext c, Hooks hooks) throws IOException {
- SSLEngine[] engines = connect(c, hooks);
+ return create(c, hooks, null);
+ }
+
+ public static TestSSLEnginePair create(TestSSLContext c, Hooks hooks, boolean[] finished)
+ throws IOException {
+ SSLEngine[] engines = connect(c, hooks, finished);
return new TestSSLEnginePair(c, engines[0], engines[1]);
}
+ public static SSLEngine[] connect(TestSSLContext c, Hooks hooks) throws IOException {
+ return connect(c, hooks, null);
+ }
+
/**
* Create a new connected server/client engine pair within a
* existing SSLContext. Optionally specify clientCipherSuites to
@@ -59,11 +68,16 @@
* cipher suite negotiation.
*/
public static SSLEngine[] connect(final TestSSLContext c,
- Hooks hooks) throws IOException {
+ Hooks hooks,
+ boolean finished[]) throws IOException {
if (hooks == null) {
hooks = new Hooks();
}
+ // FINISHED state should be returned only once.
+ boolean[] clientFinished = new boolean[1];
+ boolean[] serverFinished = new boolean[1];
+
SSLSession session = c.clientContext.createSSLEngine().getSession();
int packetBufferSize = session.getPacketBufferSize();
@@ -73,7 +87,7 @@
int applicationBufferSize = session.getApplicationBufferSize();
ByteBuffer scratch = ByteBuffer.allocate(applicationBufferSize);
- SSLEngine client = c.clientContext.createSSLEngine();
+ SSLEngine client = c.clientContext.createSSLEngine(c.host.getHostName(), c.port);
SSLEngine server = c.serverContext.createSSLEngine();
client.setUseClientMode(true);
server.setUseClientMode(false);
@@ -93,20 +107,26 @@
progress |= handshakeCompleted(client,
clientToServer,
serverToClient,
- scratch);
+ scratch,
+ clientFinished);
}
if (!serverDone) {
progress |= handshakeCompleted(server,
serverToClient,
clientToServer,
- scratch);
+ scratch,
+ serverFinished);
}
if (!progress) {
- // let caller detect the problem, but don't just hang here
break;
}
}
+ if (finished != null) {
+ assertEquals(2, finished.length);
+ finished[0] = clientFinished[0];
+ finished[1] = serverFinished[0];
+ }
return new SSLEngine[] { server, client };
}
@@ -119,7 +139,8 @@
private static boolean handshakeCompleted(SSLEngine engine,
ByteBuffer output,
ByteBuffer input,
- ByteBuffer scratch) throws IOException {
+ ByteBuffer scratch,
+ boolean[] finished) throws IOException {
try {
// make the other side's output into our input
input.flip();
@@ -127,7 +148,7 @@
HandshakeStatus status = engine.getHandshakeStatus();
switch (status) {
- case NEED_TASK:
+ case NEED_TASK: {
boolean progress = false;
while (true) {
Runnable runnable = engine.getDelegatedTask();
@@ -137,8 +158,9 @@
runnable.run();
progress = true;
}
+ }
- case NEED_UNWRAP:
+ case NEED_UNWRAP: {
// avoid underflow
if (input.remaining() == 0) {
return false;
@@ -146,16 +168,20 @@
SSLEngineResult unwrapResult = engine.unwrap(input, scratch);
assertEquals(SSLEngineResult.Status.OK, unwrapResult.getStatus());
assertEquals(0, scratch.position());
+ assertFinishedOnce(finished, unwrapResult);
return true;
+ }
- case NEED_WRAP:
+ case NEED_WRAP: {
// avoid possible overflow
if (output.remaining() != output.capacity()) {
return false;
}
SSLEngineResult wrapResult = engine.wrap(EMPTY_BYTE_BUFFER, output);
assertEquals(SSLEngineResult.Status.OK, wrapResult.getStatus());
+ assertFinishedOnce(finished, wrapResult);
return true;
+ }
case NOT_HANDSHAKING:
// should have been checked by caller before calling
@@ -170,4 +196,11 @@
input.compact();
}
}
+
+ private static void assertFinishedOnce(boolean[] finishedOut, SSLEngineResult result) {
+ if (result.getHandshakeStatus() == HandshakeStatus.FINISHED) {
+ assertFalse("should only return FINISHED once", finishedOut[0]);
+ finishedOut[0] = true;
+ }
+ }
}
diff --git a/support/src/test/java/libcore/javax/net/ssl/TestTrustManager.java b/support/src/test/java/libcore/javax/net/ssl/TestTrustManager.java
index c3511b4..b703984 100644
--- a/support/src/test/java/libcore/javax/net/ssl/TestTrustManager.java
+++ b/support/src/test/java/libcore/javax/net/ssl/TestTrustManager.java
@@ -17,7 +17,10 @@
package libcore.javax.net.ssl;
import java.io.PrintStream;
+import java.net.Socket;
+import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -26,15 +29,16 @@
/**
* TestTrustManager is a simple proxy class that wraps an existing
- * X509TrustManager to provide debug logging and recording of
+ * X509ExtendedTrustManager to provide debug logging and recording of
* values.
*/
-public final class TestTrustManager implements X509TrustManager {
+public final class TestTrustManager extends X509ExtendedTrustManager {
private static final boolean LOG = false;
private static final PrintStream out = LOG ? System.out : new NullPrintStream();
private final X509TrustManager trustManager;
+ private final X509ExtendedTrustManager extendedTrustManager;
public static TrustManager[] wrap(TrustManager[] trustManagers) {
TrustManager[] result = trustManagers.clone();
@@ -45,14 +49,23 @@
}
public static TrustManager wrap(TrustManager trustManager) {
- if (!(trustManager instanceof X509TrustManager)) {
- return trustManager;
+ if (trustManager instanceof X509ExtendedTrustManager) {
+ return new TestTrustManager((X509ExtendedTrustManager) trustManager);
+ } else if (trustManager instanceof X509TrustManager) {
+ return new TestTrustManager((X509TrustManager) trustManager);
}
- return new TestTrustManager((X509TrustManager) trustManager);
+ return trustManager;
+ }
+
+ public TestTrustManager(X509ExtendedTrustManager trustManager) {
+ out.println("TestTrustManager.<init> extendedTrustManager=" + trustManager);
+ this.extendedTrustManager = trustManager;
+ this.trustManager = trustManager;
}
public TestTrustManager(X509TrustManager trustManager) {
out.println("TestTrustManager.<init> trustManager=" + trustManager);
+ this.extendedTrustManager = null;
this.trustManager = trustManager;
}
@@ -71,6 +84,50 @@
}
}
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
+ throws CertificateException {
+ if (extendedTrustManager == null) {
+ out.print("(fallback to X509TrustManager) ");
+ checkClientTrusted(chain, authType);
+ return;
+ }
+ out.print("TestTrustManager.checkClientTrusted "
+ + "chain=" + chain.length + " "
+ + "authType=" + authType + " "
+ + "socket=" + socket + " ");
+ try {
+ assertClientAuthType(authType);
+ extendedTrustManager.checkClientTrusted(chain, authType, socket);
+ out.println("OK");
+ } catch (CertificateException e) {
+ e.printStackTrace(out);
+ throw e;
+ }
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+ throws CertificateException {
+ if (extendedTrustManager == null) {
+ out.print("(fallback to X509TrustManager) ");
+ checkClientTrusted(chain, authType);
+ return;
+ }
+ out.print("TestTrustManager.checkClientTrusted "
+ + "chain=" + chain.length + " "
+ + "authType=" + authType + " "
+ + "engine=" + engine + " ");
+ try {
+ assertClientAuthType(authType);
+ extendedTrustManager.checkClientTrusted(chain, authType, engine);
+ out.println("OK");
+ } catch (CertificateException e) {
+ e.printStackTrace(out);
+ throw e;
+ }
+ }
+
private void assertClientAuthType(String authType) {
if (!StandardNames.CLIENT_AUTH_TYPES.contains(authType)) {
throw new AssertionError("Unexpected client auth type " + authType);
@@ -92,6 +149,50 @@
}
}
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
+ throws CertificateException {
+ if (extendedTrustManager == null) {
+ out.print("(fallback to X509TrustManager) ");
+ checkServerTrusted(chain, authType);
+ return;
+ }
+ out.print("TestTrustManager.checkServerTrusted "
+ + "chain=" + chain.length + " "
+ + "authType=" + authType + " "
+ + "socket=" + socket.toString() + " ");
+ try {
+ assertServerAuthType(authType);
+ extendedTrustManager.checkServerTrusted(chain, authType, socket);
+ out.println("OK");
+ } catch (CertificateException e) {
+ e.printStackTrace(out);
+ throw e;
+ }
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+ throws CertificateException {
+ if (extendedTrustManager == null) {
+ out.print("(fallback to X509TrustManager) ");
+ checkServerTrusted(chain, authType);
+ return;
+ }
+ out.print("TestTrustManager.checkServerTrusted "
+ + "chain=" + chain.length + " "
+ + "authType=" + authType + " "
+ + "engine=" + engine.toString() + " ");
+ try {
+ assertServerAuthType(authType);
+ extendedTrustManager.checkServerTrusted(chain, authType, engine);
+ out.println("OK");
+ } catch (CertificateException e) {
+ e.printStackTrace(out);
+ throw e;
+ }
+ }
+
private void assertServerAuthType(String authType) {
if (!StandardNames.SERVER_AUTH_TYPES.contains(authType)) {
throw new AssertionError("Unexpected server auth type " + authType);
diff --git a/support/src/test/java/tests/io/MockOs.java b/support/src/test/java/tests/io/MockOs.java
index d7e284f..b1c6cee 100644
--- a/support/src/test/java/tests/io/MockOs.java
+++ b/support/src/test/java/tests/io/MockOs.java
@@ -16,6 +16,8 @@
package tests.io;
+import android.system.ErrnoException;
+import android.system.OsConstants;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -24,10 +26,8 @@
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
-import libcore.io.ErrnoException;
import libcore.io.Libcore;
import libcore.io.Os;
-import libcore.io.OsConstants;
/**
* A mocking interceptor that wraps another {@link Os} to add faults. This can
diff --git a/support/src/test/java/tests/resources/cts_dalvikExecTest.jar b/support/src/test/java/tests/resources/cts_dalvikExecTest.jar
deleted file mode 100644
index b6af791..0000000
--- a/support/src/test/java/tests/resources/cts_dalvikExecTest.jar
+++ /dev/null
Binary files differ
diff --git a/support/src/test/java/tests/resources/cts_dalvikExecTest_classes.dex b/support/src/test/java/tests/resources/cts_dalvikExecTest_classes.dex
deleted file mode 100644
index 415c50a..0000000
--- a/support/src/test/java/tests/resources/cts_dalvikExecTest_classes.dex
+++ /dev/null
Binary files differ
diff --git a/support/src/test/java/tests/support/Support_TestWebData.java b/support/src/test/java/tests/support/Support_TestWebData.java
index 9fdd021..e7f13e8 100644
--- a/support/src/test/java/tests/support/Support_TestWebData.java
+++ b/support/src/test/java/tests/support/Support_TestWebData.java
@@ -19,7 +19,6 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
@@ -31,6 +30,12 @@
public final static byte[] test1 = utfBytes();
public final static byte[] test2 = newBinaryFile(8192);
+ // Although the tests report the content length of this file as being large, a file is created
+ // that is much shorter. Actually creating a large file is not practical during tests. This
+ // file should only be used for checking header information.
+ private static final long TEST_3_CONTENT_LENGTH = ((long) Integer.MAX_VALUE) + 1;
+ public final static byte[] test3 = newBinaryFile(3);
+
private static byte[] utfBytes() {
try {
return "<html>\n<body>\n<h1>Hello World!</h1>\n</body>\n</html>\n".getBytes("UTF-8");
@@ -53,7 +58,8 @@
// Array of all test data
public final static byte[][] tests = {
test1,
- test2
+ test2,
+ test3,
};
/**
@@ -62,7 +68,9 @@
public static Support_TestWebData[] testParams = {
new Support_TestWebData(test1.length, 14000000, "test1", "text/html", false, 0),
new Support_TestWebData(test2.length, 14000002, "test2", "unknown/unknown", false,
- new Date().getTime() + 100000)
+ new Date().getTime() + 100000),
+ new Support_TestWebData(TEST_3_CONTENT_LENGTH, 14000004, "test3", "unknown/unknown", false,
+ new Date().getTime() + 100000),
};
/**
@@ -85,7 +93,7 @@
* Creates a data package with information used by the server when responding
* to requests
*/
- Support_TestWebData(int length, int lastModified, String name, String type, boolean isDir, long expDate) {
+ Support_TestWebData(long length, int lastModified, String name, String type, boolean isDir, long expDate) {
testLength = length;
testLastModified = lastModified;
testName = name;
diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java
index 32e4f20..41db89c 100644
--- a/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java
+++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java
@@ -20,18 +20,13 @@
*/
public class XmlPullParserFactory {
- // TODO: Deprecate or remove these fields. They're currently unused
- // but are public APIs.
- /** Currently unused. */
- public static final String PROPERTY_NAME =
- "org.xmlpull.v1.XmlPullParserFactory";
- /** Currently unused */
+ public static final String PROPERTY_NAME = "org.xmlpull.v1.XmlPullParserFactory";
+ protected ArrayList parserClasses;
+ protected ArrayList serializerClasses;
+
+ /** Unused, but we have to keep it because it's public API. */
protected String classNamesLocation = null;
- /** Currently unused */
- protected ArrayList parserClasses = null;
- /** Currently unused */
- protected ArrayList serializerClasses = null;
// features are kept there
// TODO: This can't be made final because it's a public API.
@@ -40,12 +35,18 @@
/**
* Protected constructor to be called by factory implementations.
*/
-
protected XmlPullParserFactory() {
+ parserClasses = new ArrayList<String>();
+ serializerClasses = new ArrayList<String>();
+
+ try {
+ parserClasses.add(Class.forName("org.kxml2.io.KXmlParser"));
+ serializerClasses.add(Class.forName("org.kxml2.io.KXmlSerializer"));
+ } catch (ClassNotFoundException e) {
+ throw new AssertionError();
+ }
}
-
-
/**
* Set the features to be set when XML Pull Parser is created by this factory.
* <p><b>NOTE:</b> factory features are not used for XML Serializer.
@@ -53,7 +54,6 @@
* @param name string with URI identifying feature
* @param state if true feature will be set; if false will be ignored
*/
-
public void setFeature(String name, boolean state) throws XmlPullParserException {
features.put(name, state);
}
@@ -67,8 +67,7 @@
* @return The value of named feature.
* Unknown features are <string>always</strong> returned as false
*/
-
- public boolean getFeature (String name) {
+ public boolean getFeature(String name) {
Boolean value = features.get(name);
return value != null ? value.booleanValue() : false;
}
@@ -81,7 +80,6 @@
* @param awareness true if the parser produced by this code
* will provide support for XML namespaces; false otherwise.
*/
-
public void setNamespaceAware(boolean awareness) {
features.put (XmlPullParser.FEATURE_PROCESS_NAMESPACES, awareness);
}
@@ -94,12 +92,10 @@
* @return true if the factory is configured to produce parsers
* which are namespace aware; false otherwise.
*/
-
public boolean isNamespaceAware() {
- return getFeature (XmlPullParser.FEATURE_PROCESS_NAMESPACES);
+ return getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES);
}
-
/**
* Specifies that the parser produced by this factory will be validating
* (it simply set feature XmlPullParser.FEATURE_VALIDATION to true or false).
@@ -108,9 +104,8 @@
*
* @param validating - if true the parsers created by this factory must be validating.
*/
-
public void setValidating(boolean validating) {
- features.put (XmlPullParser.FEATURE_VALIDATION, validating);
+ features.put(XmlPullParser.FEATURE_VALIDATION, validating);
}
/**
@@ -122,7 +117,7 @@
*/
public boolean isValidating() {
- return getFeature (XmlPullParser.FEATURE_VALIDATION);
+ return getFeature(XmlPullParser.FEATURE_VALIDATION);
}
/**
@@ -131,16 +126,80 @@
*
* @return A new instance of a XML Pull Parser.
*/
-
public XmlPullParser newPullParser() throws XmlPullParserException {
- final XmlPullParser pp = new KXmlParser();
+ final XmlPullParser pp = getParserInstance();
for (Map.Entry<String, Boolean> entry : features.entrySet()) {
- pp.setFeature(entry.getKey(), entry.getValue());
+ // NOTE: This test is needed for compatibility reasons. We guarantee
+ // that we only set a feature on a parser if its value is true.
+ if (entry.getValue()) {
+ pp.setFeature(entry.getKey(), entry.getValue());
+ }
}
return pp;
}
+ private XmlPullParser getParserInstance() throws XmlPullParserException {
+ ArrayList<Exception> exceptions = null;
+
+ if (parserClasses != null && !parserClasses.isEmpty()) {
+ exceptions = new ArrayList<Exception>();
+ for (Object o : parserClasses) {
+ try {
+ if (o != null) {
+ Class<?> parserClass = (Class<?>) o;
+ return (XmlPullParser) parserClass.newInstance();
+ }
+ } catch (InstantiationException e) {
+ exceptions.add(e);
+ } catch (IllegalAccessException e) {
+ exceptions.add(e);
+ } catch (ClassCastException e) {
+ exceptions.add(e);
+ }
+ }
+ }
+
+ throw newInstantiationException("Invalid parser class list", exceptions);
+ }
+
+ private XmlSerializer getSerializerInstance() throws XmlPullParserException {
+ ArrayList<Exception> exceptions = null;
+
+ if (serializerClasses != null && !serializerClasses.isEmpty()) {
+ exceptions = new ArrayList<Exception>();
+ for (Object o : serializerClasses) {
+ try {
+ if (o != null) {
+ Class<?> serializerClass = (Class<?>) o;
+ return (XmlSerializer) serializerClass.newInstance();
+ }
+ } catch (InstantiationException e) {
+ exceptions.add(e);
+ } catch (IllegalAccessException e) {
+ exceptions.add(e);
+ } catch (ClassCastException e) {
+ exceptions.add(e);
+ }
+ }
+ }
+
+ throw newInstantiationException("Invalid serializer class list", exceptions);
+ }
+
+ private static XmlPullParserException newInstantiationException(String message,
+ ArrayList<Exception> exceptions) {
+ if (exceptions == null || exceptions.isEmpty()) {
+ return new XmlPullParserException(message);
+ } else {
+ XmlPullParserException exception = new XmlPullParserException(message);
+ for (Exception ex : exceptions) {
+ exception.addSuppressed(ex);
+ }
+
+ return exception;
+ }
+ }
/**
* Creates a new instance of a XML Serializer.
@@ -153,7 +212,7 @@
*/
public XmlSerializer newSerializer() throws XmlPullParserException {
- return new KXmlSerializer();
+ return getSerializerInstance();
}
/**
diff --git a/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java b/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java
index 88cffd7..7b5b440 100644
--- a/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java
+++ b/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java
@@ -36,7 +36,7 @@
/**
* SAX2 Driver that pulls events from XmlPullParser
- * and comverts them into SAX2 callbacks.
+ * and converts them into SAX2 callbacks.
*
* @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a>
*/