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 &lt;command&gt;" 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">&lt;netdb.h&gt;</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">&lt;fcntl.h&gt;</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">&lt;sys/socket.h&gt;</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">&lt;pwd.h&gt;</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">&lt;poll.h&gt;</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">&lt;stat.h&gt;</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">&lt;sys/time.h&gt;</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">&lt;sys/utsname.h&gt;</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 &lt; rhs, and greater than 0 if lhs &gt; rhs.
+     * @return 0 if lhs = rhs, less than 0 if lhs &lt; rhs, and greater than 0
+     *         if lhs &gt; 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 @@
  * "&#92;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 &amp;} 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>
- * &lt;modifier&gt;
- * &lt;relation&gt; &lt;text-argument&gt;
- * &lt;reset&gt; &lt;text-argument&gt;
+ * &amp;AE&lt;&lt;ä &lt;&lt;&lt;Ä
+ * &amp;z&lt;å&lt;&lt;&lt;Å&lt;&lt;&lt;aa&lt;&lt;&lt;Aa&lt;&lt;&lt;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>'&lt;' : 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 '&amp;' 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>'&amp;' : 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">&lt;netdb.h&gt;</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">&lt;poll.h&gt;</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">&lt;stat.h&gt;</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">&lt;sys/time.h&gt;</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">&lt;sys/utsname.h&gt;</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>
- *
- * &nbsp;  1. It can be created over the list of X.509 certificates
- * (implementations of X509Certificate class) provided in constructor.<br>
- *
- * &nbsp;  2. It can be created by means of <code>getInstance</code> methods
- * on the base of the following ASN.1 DER encoded forms:<br>
- *
- * &nbsp;&nbsp;  - 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>
- * &nbsp;&nbsp;  - 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>
- * &nbsp;
- */
-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(), &times) == 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(), &times) == 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>
  */