Merge "Refactor ZoneInfoTest to use java.time types"
diff --git a/JavaLibrary.bp b/JavaLibrary.bp
index e763224..066a0ac 100644
--- a/JavaLibrary.bp
+++ b/JavaLibrary.bp
@@ -467,6 +467,7 @@
         "harmony-tests/src/test/java/**/*.java",
         "json/src/test/java/**/*.java",
         "luni/src/test/java/**/*.java",
+        "test-rules/src/test/java/**/*.java",
         "xml/src/test/java/**/*.java",
     ],
     exclude_srcs: [
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index 663a7c8..9cf5f80 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -172,8 +172,9 @@
      */
     public BaseDexClassLoader(ByteBuffer[] dexFiles, String librarySearchPath, ClassLoader parent) {
         super(parent);
-        this.pathList = new DexPathList(this, dexFiles, librarySearchPath);
         this.sharedLibraryLoaders = null;
+        this.pathList = new DexPathList(this, librarySearchPath);
+        this.pathList.initByteBufferDexPath(dexFiles);
     }
 
     @Override
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 535257f..8d32931 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -102,15 +102,16 @@
      * @param elements
      *            the temporary dex path list elements from DexPathList.makeElements
      */
-    DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
+    DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements)
+            throws IOException {
         mCookie = openDexFile(fileName, null, 0, loader, elements);
         mInternalCookie = mCookie;
         mFileName = fileName;
         //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
     }
 
-    DexFile(ByteBuffer buf) throws IOException {
-        mCookie = openInMemoryDexFile(buf);
+    DexFile(ByteBuffer[] bufs) throws IOException {
+        mCookie = openInMemoryDexFiles(bufs);
         mInternalCookie = mCookie;
         mFileName = null;
     }
@@ -369,16 +370,23 @@
                                  elements);
     }
 
-    private static Object openInMemoryDexFile(ByteBuffer buf) throws IOException {
-        if (buf.isDirect()) {
-            return createCookieWithDirectBuffer(buf, buf.position(), buf.limit());
-        } else {
-            return createCookieWithArray(buf.array(), buf.position(), buf.limit());
+    private static Object openInMemoryDexFiles(ByteBuffer[] bufs) throws IOException {
+        // Preprocess the ByteBuffers for openInMemoryDexFilesNative. We extract
+        // the backing array (non-direct buffers only) and start/end positions
+        // so that the native method does not have to call Java methods anymore.
+        byte[][] arrays = new byte[bufs.length][];
+        int[] starts = new int[bufs.length];
+        int[] ends = new int[bufs.length];
+        for (int i = 0; i < bufs.length; ++i) {
+            arrays[i] = bufs[i].isDirect() ? null : bufs[i].array();
+            starts[i] = bufs[i].position();
+            ends[i] = bufs[i].limit();
         }
+        return openInMemoryDexFilesNative(bufs, arrays, starts, ends);
     }
 
-    private static native Object createCookieWithDirectBuffer(ByteBuffer buf, int start, int end);
-    private static native Object createCookieWithArray(byte[] buf, int start, int end);
+    private static native Object openInMemoryDexFilesNative(ByteBuffer[] bufs, byte[][] arrays,
+            int[] starts, int[] ends);
 
     /*
      * Returns true if the dex file is backed by a valid oat file.
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index 6340b77..fc57dc0 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -49,8 +49,10 @@
  *
  * <p>This class also contains methods to use these lists to look up
  * classes and resources.</p>
+ *
+ * @hide
  */
-/*package*/ final class DexPathList {
+public final class DexPathList {
     private static final String DEX_SUFFIX = ".dex";
     private static final String zipSeparator = "!/";
 
@@ -99,33 +101,16 @@
      *
      * @param dexFiles the bytebuffers containing the dex files that we should load classes from.
      */
-    public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles,
-            String librarySearchPath) {
+    public DexPathList(ClassLoader definingContext, String librarySearchPath) {
         if (definingContext == null) {
             throw new NullPointerException("definingContext == null");
         }
-        if (dexFiles == null) {
-            throw new NullPointerException("dexFiles == null");
-        }
-        if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) {
-            throw new NullPointerException("dexFiles contains a null Buffer!");
-        }
 
         this.definingContext = definingContext;
-
         this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
         this.systemNativeLibraryDirectories =
                 splitPaths(System.getProperty("java.library.path"), true);
         this.nativeLibraryPathElements = makePathElements(getAllNativeLibraryDirectories());
-
-        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
-        this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);
-        if (suppressedExceptions.size() > 0) {
-            this.dexElementsSuppressedExceptions =
-                    suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
-        } else {
-            dexElementsSuppressedExceptions = null;
-        }
     }
 
     /**
@@ -261,6 +246,41 @@
     }
 
     /**
+     * For InMemoryDexClassLoader. Initializes {@code dexElements} with dex files
+     * loaded from {@code dexFiles} buffers.
+     *
+     * @param dexFiles ByteBuffers containing raw dex data. Apks are not supported.
+     */
+    /* package */ void initByteBufferDexPath(ByteBuffer[] dexFiles) {
+        if (dexFiles == null) {
+            throw new NullPointerException("dexFiles == null");
+        }
+        if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) {
+            throw new NullPointerException("dexFiles contains a null Buffer!");
+        }
+        if (dexElements != null || dexElementsSuppressedExceptions != null) {
+            throw new IllegalStateException("Should only be called once");
+        }
+
+        final List<IOException> suppressedExceptions = new ArrayList<IOException>();
+
+        try {
+            Element[] null_elements = null;
+            DexFile dex = new DexFile(dexFiles);
+            dexElements = new Element[] { new Element(dex) };
+        } catch (IOException suppressed) {
+            System.logE("Unable to load dex files", suppressed);
+            suppressedExceptions.add(suppressed);
+            dexElements = new Element[0];
+        }
+
+        if (suppressedExceptions.size() > 0) {
+            dexElementsSuppressedExceptions = suppressedExceptions.toArray(
+                    new IOException[suppressedExceptions.size()]);
+        }
+    }
+
+    /**
      * Splits the given dex path string into elements using the path
      * separator, pruning out any elements that do not refer to existing
      * and readable files.
@@ -301,14 +321,16 @@
         return result;
     }
 
+    // This method is not used anymore. Kept around only because there are many legacy users of it.
+    @SuppressWarnings("unused")
     @UnsupportedAppUsage
-    private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
+    public static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
             List<IOException> suppressedExceptions) {
         Element[] elements = new Element[dexFiles.length];
         int elementPos = 0;
         for (ByteBuffer buf : dexFiles) {
             try {
-                DexFile dex = new DexFile(buf);
+                DexFile dex = new DexFile(new ByteBuffer[] { buf });
                 elements[elementPos++] = new Element(dex);
             } catch (IOException suppressed) {
                 System.logE("Unable to load dex file: " + buf, suppressed);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
index 6253b6b..16a7415 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
@@ -18,7 +18,6 @@
 package org.apache.harmony.tests.java.io;
 
 import dalvik.system.DexFile;
-import dalvik.system.VMRuntime;
 import java.io.Externalizable;
 import java.io.File;
 import java.io.IOException;
@@ -28,15 +27,21 @@
 import java.io.ObjectStreamClass;
 import java.io.ObjectStreamField;
 import java.io.Serializable;
-import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
-import junit.framework.TestCase;
+import libcore.junit.junit3.TestCaseWithRules;
+import libcore.junit.util.SwitchTargetSdkVersionRule;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
 
-public class ObjectStreamClassTest extends TestCase {
+public class ObjectStreamClassTest extends TestCaseWithRules {
+
+    @Rule
+    public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
 
     static class DummyClass implements Serializable {
         private static final long serialVersionUID = 999999999999999L;
@@ -228,102 +233,55 @@
     }
 
     // http://b/28106822
-    public void testBug28106822() throws Exception {
-        int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
+    @TargetSdkVersion(24)
+    public void testBug28106822_target24() throws Exception {
+        // Assert behavior up to 24
+        Method getConstructorId = getConstructorIdMethod();
+
+        assertEquals(1189998819991197253L, getConstructorId.invoke(null, Object.class));
+        assertEquals(1189998819991197253L, getConstructorId.invoke(null, String.class));
+
+        Method newInstance = getNewInstanceMethod();
+
+        Object obj = newInstance.invoke(null, String.class, 0 /* ignored */);
+        assertNotNull(obj);
+        assertTrue(obj instanceof String);
+    }
+
+    // http://b/28106822
+    @TargetSdkVersion(25)
+    public void testBug28106822_target25() throws Exception {
+        // Assert behavior from API 25
+        Method getConstructorId = getConstructorIdMethod();
+
+        Method newInstance = getNewInstanceMethod();
+
         try {
-            // Assert behavior up to 24
-            VMRuntime.getRuntime().setTargetSdkVersion(24);
-            Method getConstructorId = ObjectStreamClass.class.getDeclaredMethod(
-                    "getConstructorId", Class.class);
-            getConstructorId.setAccessible(true);
-
-            assertEquals(1189998819991197253L, getConstructorId.invoke(null, Object.class));
-            assertEquals(1189998819991197253L, getConstructorId.invoke(null, String.class));
-
-            Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance",
-                    Class.class, Long.TYPE);
-            newInstance.setAccessible(true);
-
-            Object obj = newInstance.invoke(null, String.class, 0 /* ignored */);
-            assertNotNull(obj);
-            assertTrue(obj instanceof String);
-
-            // Assert behavior from API 25
-            VMRuntime.getRuntime().setTargetSdkVersion(25);
-            try {
-                getConstructorId.invoke(null, Object.class);
-                fail();
-            } catch (InvocationTargetException expected) {
-                assertTrue(expected.getCause() instanceof UnsupportedOperationException);
-            }
-            try {
-                newInstance.invoke(null, String.class, 0 /* ignored */);
-                fail();
-            } catch (InvocationTargetException expected) {
-                assertTrue(expected.getCause() instanceof UnsupportedOperationException);
-            }
-
-        } finally {
-            VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion);
+            getConstructorId.invoke(null, Object.class);
+            fail();
+        } catch (InvocationTargetException expected) {
+            assertTrue(expected.getCause() instanceof UnsupportedOperationException);
+        }
+        try {
+            newInstance.invoke(null, String.class, 0 /* ignored */);
+            fail();
+        } catch (InvocationTargetException expected) {
+            assertTrue(expected.getCause() instanceof UnsupportedOperationException);
         }
     }
 
-    // Class without <clinit> method
-    public static class NoClinitParent {
-    }
-    // Class without <clinit> method
-    public static class NoClinitChildWithNoClinitParent extends NoClinitParent {
+    private Method getConstructorIdMethod() throws NoSuchMethodException {
+        Method getConstructorId = ObjectStreamClass.class.getDeclaredMethod(
+            "getConstructorId", Class.class);
+        getConstructorId.setAccessible(true);
+        return getConstructorId;
     }
 
-    // Class with <clinit> method
-    public static class ClinitParent {
-        // This field will trigger creation of <clinit> method for this class
-        private static final String TAG = ClinitParent.class.getName();
-        static {
-
-        }
-    }
-    // Class without <clinit> but with parent that has <clinit> method
-    public static class NoClinitChildWithClinitParent extends ClinitParent {
-    }
-
-    // http://b/29064453
-    public void testHasClinit() throws Exception {
-        Method hasStaticInitializer =
-            ObjectStreamClass.class.getDeclaredMethod("hasStaticInitializer", Class.class,
-                                                      boolean.class);
-        hasStaticInitializer.setAccessible(true);
-
-        assertTrue((Boolean)
-                   hasStaticInitializer.invoke(null, ClinitParent.class,
-                                               false /* checkSuperclass */));
-
-        // RI will return correctly False in this case, but android has been returning true
-        // in this particular case. We're returning true to enable deserializing classes
-        // like NoClinitChildWithClinitParent without explicit serialVersionID field.
-        assertTrue((Boolean)
-                   hasStaticInitializer.invoke(null, NoClinitChildWithClinitParent.class,
-                                               false /* checkSuperclass */));
-        assertFalse((Boolean)
-                    hasStaticInitializer.invoke(null, NoClinitParent.class,
-                                                false /* checkSuperclass */));
-        assertFalse((Boolean)
-                    hasStaticInitializer.invoke(null, NoClinitChildWithNoClinitParent.class,
-                                                false /* checkSuperclass */));
-
-
-        assertTrue((Boolean)
-                   hasStaticInitializer.invoke(null, ClinitParent.class,
-                                               true /* checkSuperclass */));
-        assertFalse((Boolean)
-                   hasStaticInitializer.invoke(null, NoClinitChildWithClinitParent.class,
-                                               true /* checkSuperclass */));
-        assertFalse((Boolean)
-                    hasStaticInitializer.invoke(null, NoClinitParent.class,
-                                                true /* checkSuperclass */));
-        assertFalse((Boolean)
-                    hasStaticInitializer.invoke(null, NoClinitChildWithNoClinitParent.class,
-                                                true /* checkSuperclass */));
+    private Method getNewInstanceMethod() throws NoSuchMethodException {
+        Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance",
+            Class.class, Long.TYPE);
+        newInstance.setAccessible(true);
+        return newInstance;
     }
 
     // http://b/29721023
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index f8f37fd..900af30 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -463,11 +463,7 @@
      */
     @UnsupportedAppUsage
     @libcore.api.CorePlatformApi
-    public void registerNativeAllocation(long bytes) {
-        // TODO: Change the runtime to support passing the size as a long instead
-        // of an int. For now, we clamp the size to fit.
-        registerNativeAllocationInternal((int)Math.min(bytes, Integer.MAX_VALUE));
-    }
+    public native void registerNativeAllocation(long bytes);
 
     /**
      * Backward compatibility version of registerNativeAllocation. We used to pass an int instead
@@ -481,16 +477,12 @@
         registerNativeAllocation((long) bytes);
     }
 
-    private native void registerNativeAllocationInternal(int bytes);
-
     /**
      * Registers a native free by reducing the number of native bytes accounted for.
      */
     @UnsupportedAppUsage
     @libcore.api.CorePlatformApi
-    public void registerNativeFree(long bytes) {
-        registerNativeFreeInternal((int)Math.min(bytes, Integer.MAX_VALUE));
-    }
+    public native void registerNativeFree(long bytes);
 
     /**
      * Backward compatibility version of registerNativeFree.
@@ -502,7 +494,6 @@
     public void registerNativeFree(int bytes) {
         registerNativeFree((long) bytes);
     }
-    private native void registerNativeFreeInternal(int bytes);
 
     /**
      * Return the number of native objects that are reported by a single call to
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index 9066ddb..d190166 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -17,6 +17,11 @@
 package android.system;
 
 import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import libcore.io.Libcore;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
 import java.net.InetAddress;
@@ -24,7 +29,6 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
-import libcore.io.Libcore;
 
 /**
  * Access to low-level system functionality. Most of these are system calls. Most users will want
@@ -57,7 +61,7 @@
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/bind.2.html">bind(2)</a>.
      */
-    public static void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { Libcore.os.bind(fd, address); }
+    public static void bind(@NonNull FileDescriptor fd, @NonNull SocketAddress address) throws ErrnoException, SocketException { Libcore.os.bind(fd, address); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
@@ -103,7 +107,7 @@
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/connect.2.html">connect(2)</a>.
      */
-    public static void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { Libcore.os.connect(fd, address); }
+    public static void connect(@NonNull FileDescriptor fd, @NonNull SocketAddress address) throws ErrnoException, SocketException { Libcore.os.connect(fd, address); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/dup.2.html">dup(2)</a>.
@@ -512,7 +516,7 @@
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/sendto.2.html">sendto(2)</a>.
      */
-    public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
+    public static int sendto(@NonNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount, int flags, @Nullable SocketAddress address) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/setegid.2.html">setegid(2)</a>.
diff --git a/luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java b/luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java
index 393874b..592fac1 100644
--- a/luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java
+++ b/luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java
@@ -28,6 +28,7 @@
 public final class TimeZoneDataFiles {
     private static final String ANDROID_ROOT_ENV = "ANDROID_ROOT";
     private static final String ANDROID_RUNTIME_ROOT_ENV = "ANDROID_RUNTIME_ROOT";
+    private static final String ANDROID_TZDATA_ROOT_ENV = "ANDROID_TZDATA_ROOT";
     private static final String ANDROID_DATA_ENV = "ANDROID_DATA";
 
     private TimeZoneDataFiles() {}
@@ -65,7 +66,7 @@
     }
 
     public static String getTimeZoneModuleFile(String fileName) {
-        return "/apex/com.android.tzdata/etc/" + fileName;
+        return System.getenv(ANDROID_TZDATA_ROOT_ENV) + "/etc/" + fileName;
     }
 
     // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 18a60ab..0b4a94a 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 
 #include <log/log.h>
+#include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalFrame.h>
 
 #include "JniConstants.h"
@@ -57,21 +58,15 @@
     return JNI_VERSION_1_6;
 }
 
-// DalvikVM calls this on shutdown, do any global cleanup here.
-// -- Very important if we restart multiple DalvikVMs in the same process to reset the state.
-void JNI_OnUnload(JavaVM* vm, void*) {
-    JNIEnv* env;
-    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        ALOGE("JavaVM::GetEnv() failed");
-        abort();
-    }
+// ART calls this on shutdown, do any global cleanup here.
+// -- Very important if we restart multiple ART runtimes in the same process to reset the state.
+void JNI_OnUnload(JavaVM*, void*) {
+    // Don't use the JavaVM in this method. ART only calls this once all threads are
+    // unregistered.
     ALOGV("libjavacore JNI_OnUnload");
-
-    ScopedLocalFrame localFrame(env);
-
-#define UNREGISTER(FN) extern void FN(JNIEnv*); FN(env)
+#define UNREGISTER(FN) extern void FN(); FN()
     UNREGISTER(unregister_libcore_icu_ICU);
 #undef UNREGISTER
-
     JniConstants::Invalidate();
+    jniUninitializeConstants();
 }
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 356ae03..8a571fd 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -1039,16 +1039,24 @@
 
   // Returns a string containing the expected path of the (optional) /apex tz module data file
   static std::string getTimeZoneModulePath() {
-    std::string apexPath = "/apex/com.android.tzdata/etc/icu/icu_tzdata.dat";
-    return apexPath;
+    const char* tzdataModulePathPrefix = getenv("ANDROID_TZDATA_ROOT");
+    if (tzdataModulePathPrefix == NULL) {
+      ALOGE("ANDROID_TZDATA_ROOT environment variable not set"); \
+      abort();
+    }
+
+    std::string tzdataModulePath;
+    tzdataModulePath = tzdataModulePathPrefix;
+    tzdataModulePath += "/etc/icu/icu_tzdata.dat";
+    return tzdataModulePath;
   }
 
   static std::string getRuntimeModulePath() {
-      const char* runtimeModulePathPrefix = getenv("ANDROID_RUNTIME_ROOT");
-      if (runtimeModulePathPrefix == NULL) {
-        ALOGE("ANDROID_RUNTIME_ROOT environment variable not set"); \
-        abort();
-      }
+    const char* runtimeModulePathPrefix = getenv("ANDROID_RUNTIME_ROOT");
+    if (runtimeModulePathPrefix == NULL) {
+      ALOGE("ANDROID_RUNTIME_ROOT environment variable not set"); \
+      abort();
+    }
 
     std::string runtimeModulePath;
     runtimeModulePath = runtimeModulePathPrefix;
@@ -1073,7 +1081,7 @@
 }
 
 // De-init ICU, unloading the data files. Do the opposite of the above function.
-void unregister_libcore_icu_ICU(JNIEnv*) {
+void unregister_libcore_icu_ICU() {
   // Explicitly calling this is optional. Dlclose will take care of it as well.
   sIcuRegistration.reset();
 }
diff --git a/luni/src/main/native/libcore_io_Linux.cpp b/luni/src/main/native/libcore_io_Linux.cpp
index 635619a..4072b6b 100644
--- a/luni/src/main/native/libcore_io_Linux.cpp
+++ b/luni/src/main/native/libcore_io_Linux.cpp
@@ -987,7 +987,8 @@
 
 static void Linux_bindSocketAddress(
         JNIEnv* env, jobject thisObj, jobject javaFd, jobject javaSocketAddress) {
-    if (env->IsInstanceOf(javaSocketAddress, JniConstants::GetInetSocketAddressClass(env))) {
+    if (javaSocketAddress != NULL &&
+            env->IsInstanceOf(javaSocketAddress, JniConstants::GetInetSocketAddressClass(env))) {
         // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK.
         jobject javaInetAddress;
         jint port;
@@ -1169,7 +1170,8 @@
 
 static void Linux_connectSocketAddress(
         JNIEnv* env, jobject thisObj, jobject javaFd, jobject javaSocketAddress) {
-    if (env->IsInstanceOf(javaSocketAddress, JniConstants::GetInetSocketAddressClass(env))) {
+    if (javaSocketAddress != NULL &&
+            env->IsInstanceOf(javaSocketAddress, JniConstants::GetInetSocketAddressClass(env))) {
         // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK.
         jobject javaInetAddress;
         jint port;
@@ -2187,7 +2189,8 @@
 }
 
 static jint Linux_sendtoBytesSocketAddress(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaSocketAddress) {
-    if (env->IsInstanceOf(javaSocketAddress, JniConstants::GetInetSocketAddressClass(env))) {
+    if (javaSocketAddress != NULL &&
+            env->IsInstanceOf(javaSocketAddress, JniConstants::GetInetSocketAddressClass(env))) {
         // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK.
         jobject javaInetAddress;
         jint port;
@@ -2203,11 +2206,19 @@
 
     sockaddr_storage ss;
     socklen_t sa_len;
-    if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
-        return -1;
+    const sockaddr* sa;
+
+    if (javaSocketAddress != NULL) {
+        if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
+            return -1;
+        }
+
+        sa = reinterpret_cast<const sockaddr*>(&ss);
+    } else {
+        sa = NULL;
+        sa_len = 0;
     }
 
-    const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
     // We don't need the return value because we'll already have thrown.
     return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, sa, sa_len);
 }
diff --git a/luni/src/test/java/libcore/dalvik/system/InMemoryDexClassLoaderTest.java b/luni/src/test/java/libcore/dalvik/system/InMemoryDexClassLoaderTest.java
index ef87ee5..1012c9f 100644
--- a/luni/src/test/java/libcore/dalvik/system/InMemoryDexClassLoaderTest.java
+++ b/luni/src/test/java/libcore/dalvik/system/InMemoryDexClassLoaderTest.java
@@ -24,11 +24,13 @@
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.file.Files;
+import java.util.ArrayList;
 import libcore.io.Streams;
 import junit.framework.TestCase;
 
 import dalvik.system.BaseDexClassLoader;
 import dalvik.system.InMemoryDexClassLoader;
+import dalvik.system.DexPathList;
 
 /**
  * Tests for the class {@link InMemoryDexClassLoader}.
@@ -424,6 +426,20 @@
         classLoader.loadClass("test.TestMethods");
     }
 
+    /**
+     * DexPathList.makeInMemoryDexElements() is a legacy code path not used by
+     * InMemoryDexClassLoader anymore but heavily used by 3p apps. Test that it still works.
+     */
+    public void testMakeInMemoryDexElements() throws Exception {
+        ArrayList<IOException> exceptions = new ArrayList<>();
+        Object[] elements = DexPathList.makeInMemoryDexElements(
+                new ByteBuffer[] { readFileToByteBufferDirect(dex1),
+                                   readFileToByteBufferIndirect(dex2) },
+                exceptions);
+        assertEquals(2, elements.length);
+        assertTrue(exceptions.isEmpty());
+    }
+
     private static File makeEmptyFile(File directory, String name) throws IOException {
         assertTrue(directory.mkdirs());
         File result = new File(directory, name);
diff --git a/luni/src/test/java/libcore/java/io/ObjectStreamClassTest.java b/luni/src/test/java/libcore/java/io/ObjectStreamClassTest.java
new file mode 100644
index 0000000..612b7d8
--- /dev/null
+++ b/luni/src/test/java/libcore/java/io/ObjectStreamClassTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 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.io;
+
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import libcore.junit.util.SwitchTargetSdkVersionRule;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(JUnitParamsRunner.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ObjectStreamClassTest {
+
+  @Rule
+  public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
+
+  /**
+   * The default SUID for this should not be affected by the b/29064453 patch.
+   */
+  private static class BaseWithStaticInitializer implements Serializable {
+    static {
+      System.out.println(
+          "Static initializer for " + BaseWithoutStaticInitializer.class.getCanonicalName());
+    }
+  }
+
+  /**
+   * The default SUID for this should not be affected by the b/29064453 patch.
+   */
+  private static class BaseWithoutStaticInitializer implements Serializable {
+  }
+
+  /**
+   * The default SUID for this should not be affected by the b/29064453 patch.
+   */
+  private static class WithStaticInitializer extends BaseWithoutStaticInitializer {
+    static {
+      System.out.println(
+          "Static initializer for " + WithStaticInitializer.class.getCanonicalName());
+    }
+  }
+
+  /**
+   * The default SUID for this should not be affected by the b/29064453 patch.
+   */
+  private static class WithoutStaticInitializer extends BaseWithoutStaticInitializer {
+  }
+
+  /**
+   * The default SUID for this should be affected by the b/29064453 patch and so should differ
+   * between version <= 23 and version > 23.
+   */
+  private static class InheritStaticInitializer extends BaseWithStaticInitializer {
+  }
+
+  public static Object[][] defaultSUIDs() {
+    return new Object[][] {
+        // The default SUID for BaseWithStaticInitializer should not be affected by the b/29064453
+        // patch.
+        { BaseWithStaticInitializer.class, -4971959491244110788L, -4971959491244110788L },
+
+        // The default SUID for BaseWithoutStaticInitializer should not be affected by the
+        // b/29064453 patch.
+        { BaseWithoutStaticInitializer.class, -245652310465925293L, -245652310465925293L },
+
+        // The default SUID for WithStaticInitializer should not be affected by the b/29064453
+        // patch.
+        { WithStaticInitializer.class, -3581047773254885060L, -3581047773254885060L },
+
+        // The default SUID for WithStaticInitializer should not be affected by the
+        // b/29064453 patch.
+        { WithoutStaticInitializer.class, -975889567651927545L, -975889567651927545L },
+
+        // The default SUID for the InheritStaticInitializer should be affected by the b/29064453
+        // patch and so should differ between version <= 23 and version > 23.
+        { InheritStaticInitializer.class, 4188245044387716731L, 992629205079295334L },
+
+
+    };
+  }
+
+  @Parameters(method = "defaultSUIDs")
+  @Test
+  public void computeDefaultSUID_current(Class<?> clazz, long suid,
+      @SuppressWarnings("unused") long suid23) {
+    checkSerialVersionUID(suid, clazz);
+  }
+
+  @Parameters(method = "defaultSUIDs")
+  @Test
+  @TargetSdkVersion(23)
+  public void computeDefaultSUID_targetSdkVersion_23(Class<?> clazz,
+      @SuppressWarnings("unused") long suid, long suid23) {
+    checkSerialVersionUID(suid23, clazz);
+  }
+
+  private static void checkSerialVersionUID(long expectedSUID, Class<?> clazz) {
+    // Use reflection to access the private static computeDefaultSUID method.
+    long defaultSUID;
+    try {
+      Method computeDefaultSUIDMethod =
+          ObjectStreamClass.class.getDeclaredMethod("computeDefaultSUID", Class.class);
+      computeDefaultSUIDMethod.setAccessible(true);
+      defaultSUID = (Long) computeDefaultSUIDMethod.invoke(null, clazz);
+    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+      throw new IllegalStateException(e);
+    }
+    assertEquals(expectedSUID, defaultSUID);
+  }
+}
diff --git a/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java b/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java
index 15119bf..ec6c09b 100644
--- a/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldRuntimeTest.java
@@ -25,13 +25,19 @@
 import java.io.OutputStream;
 import java.security.Permission;
 import java.util.Arrays;
-import java.util.Vector;
+import libcore.junit.junit3.TestCaseWithRules;
+import libcore.junit.util.SwitchTargetSdkVersionRule;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
 import tests.support.resource.Support_Resources;
-import dalvik.system.VMRuntime;
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
 
-public class OldRuntimeTest extends junit.framework.TestCase {
+public class OldRuntimeTest extends TestCaseWithRules {
+
+    @Rule
+    public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
 
     Runtime r = Runtime.getRuntime();
 
@@ -517,71 +523,66 @@
     }
 
     // b/25859957
-    public void test_loadDeprecated() throws Exception {
-        final int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
+    @TargetSdkVersion(24)
+    public void test_loadDeprecated_targetSdkVersion_24() throws Exception {
         try {
-            try {
-                // Call Runtime#load(String, ClassLoader) at API level 24 (N). It will fail
-                // with a UnsatisfiedLinkError because requested library doesn't exits.
-                VMRuntime.getRuntime().setTargetSdkVersion(24);
-                Method loadMethod =
-                        Runtime.class.getDeclaredMethod("load", String.class, ClassLoader.class);
-                loadMethod.setAccessible(true);
-                loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null);
-                fail();
-            } catch(InvocationTargetException expected) {
-                assertTrue(expected.getCause() instanceof UnsatisfiedLinkError);
-            }
-
-            try {
-                // Call Runtime#load(String, ClassLoader) at API level 25. It will fail
-                // with a IllegalStateException because it's deprecated.
-                VMRuntime.getRuntime().setTargetSdkVersion(25);
-                Method loadMethod =
-                        Runtime.class.getDeclaredMethod("load", String.class, ClassLoader.class);
-                loadMethod.setAccessible(true);
-                loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null);
-                fail();
-            } catch(InvocationTargetException expected) {
-                assertTrue(expected.getCause() instanceof UnsupportedOperationException);
-            }
-        } finally {
-            VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion);
+            // Call Runtime#load(String, ClassLoader) at API level 24 (N). It will fail
+            // with an UnsatisfiedLinkError because requested library doesn't exist.
+            Method loadMethod =
+                    Runtime.class.getDeclaredMethod("load", String.class, ClassLoader.class);
+            loadMethod.setAccessible(true);
+            loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null);
+            fail();
+        } catch(InvocationTargetException expected) {
+            assertTrue(expected.getCause() instanceof UnsatisfiedLinkError);
         }
     }
 
     // b/25859957
-    public void test_loadLibraryDeprecated() throws Exception {
-        final int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
+    @TargetSdkVersion(25)
+    public void test_loadDeprecated_targetSdkVersion_25() throws Exception {
         try {
-            try {
-                // Call Runtime#loadLibrary(String, ClassLoader) at API level 24 (N). It will fail
-                // with a UnsatisfiedLinkError because requested library doesn't exits.
-                VMRuntime.getRuntime().setTargetSdkVersion(24);
-                Method loadMethod =
-                        Runtime.class.getDeclaredMethod("loadLibrary", String.class, ClassLoader.class);
-                loadMethod.setAccessible(true);
-                loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null);
-                fail();
-            } catch(InvocationTargetException expected) {
-                assertTrue(expected.getCause() instanceof UnsatisfiedLinkError);
-            }
+            // Call Runtime#load(String, ClassLoader) at API level 25. It will fail
+            // with an IllegalStateException because it's deprecated.
+            Method loadMethod =
+                    Runtime.class.getDeclaredMethod("load", String.class, ClassLoader.class);
+            loadMethod.setAccessible(true);
+            loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null);
+            fail();
+        } catch(InvocationTargetException expected) {
+            assertTrue(expected.getCause() instanceof UnsupportedOperationException);
+        }
+    }
 
-            try {
-                // Call Runtime#load(String, ClassLoader) at API level 25. It will fail
-                // with a IllegalStateException because it's deprecated.
+    // b/25859957
+    @TargetSdkVersion(24)
+    public void test_loadLibraryDeprecated_targetSdkVersion_24() throws Exception {
+        try {
+            // Call Runtime#loadLibrary(String, ClassLoader) at API level 24 (N). It will fail
+            // with a UnsatisfiedLinkError because requested library doesn't exits.
+            Method loadMethod =
+                    Runtime.class.getDeclaredMethod("loadLibrary", String.class, ClassLoader.class);
+            loadMethod.setAccessible(true);
+            loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null);
+            fail();
+        } catch(InvocationTargetException expected) {
+            assertTrue(expected.getCause() instanceof UnsatisfiedLinkError);
+        }
+    }
 
-                VMRuntime.getRuntime().setTargetSdkVersion(25);
-                Method loadMethod =
-                        Runtime.class.getDeclaredMethod("loadLibrary", String.class, ClassLoader.class);
-                loadMethod.setAccessible(true);
-                loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null);
-                fail();
-            } catch(InvocationTargetException expected) {
-                assertTrue(expected.getCause() instanceof UnsupportedOperationException);
-            }
-        } finally {
-            VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion);
+    // b/25859957
+    @TargetSdkVersion(25)
+    public void test_loadLibraryDeprecated_targetSdkVersion_25() throws Exception {
+        try {
+            // Call Runtime#load(String, ClassLoader) at API level 25. It will fail
+            // with a IllegalStateException because it's deprecated.
+            Method loadMethod =
+                    Runtime.class.getDeclaredMethod("loadLibrary", String.class, ClassLoader.class);
+            loadMethod.setAccessible(true);
+            loadMethod.invoke(Runtime.getRuntime(), "nonExistentLibrary", null);
+            fail();
+        } catch(InvocationTargetException expected) {
+            assertTrue(expected.getCause() instanceof UnsupportedOperationException);
         }
     }
 }
diff --git a/luni/src/test/java/libcore/java/lang/PackageTest.java b/luni/src/test/java/libcore/java/lang/PackageTest.java
index 176d0de..672af5d 100644
--- a/luni/src/test/java/libcore/java/lang/PackageTest.java
+++ b/luni/src/test/java/libcore/java/lang/PackageTest.java
@@ -19,9 +19,17 @@
 import dalvik.system.VMRuntime;
 import java.util.Arrays;
 import java.util.List;
-import junit.framework.TestCase;
+import libcore.junit.junit3.TestCaseWithRules;
+import libcore.junit.util.SwitchTargetSdkVersionRule;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
 
-public final class PackageTest extends TestCase {
+public final class PackageTest extends TestCaseWithRules {
+
+    @Rule
+    public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
+
     /** assign packages immediately so that Class.getPackage() calls cannot side-effect it */
     private static final List<Package> packages = Arrays.asList(Package.getPackages());
 
@@ -39,21 +47,17 @@
     }
 
     // http://b/28057303
-    public void test_toString() throws Exception {
-        int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
-        try {
-            VMRuntime.getRuntime().setTargetSdkVersion(24);
-            Package libcoreJavaLang = Package.getPackage("libcore.java.lang");
-            assertEquals("package libcore.java.lang",
-                         libcoreJavaLang.toString());
+    @TargetSdkVersion(24)
+    public void test_toString_targetSdkVersion_24() throws Exception {
+        Package libcoreJavaLang = Package.getPackage("libcore.java.lang");
+        assertEquals("package libcore.java.lang", libcoreJavaLang.toString());
+    }
 
-            VMRuntime.getRuntime().setTargetSdkVersion(25);
-            libcoreJavaLang = Package.getPackage("libcore.java.lang");
-            assertEquals("package libcore.java.lang, Unknown, version 0.0",
-                         libcoreJavaLang.toString());
-        } finally {
-            VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion);
-        }
+    // http://b/28057303
+    @TargetSdkVersion(25)
+    public void test_toString_targetSdkVersion_25() throws Exception {
+        Package libcoreJavaLang = Package.getPackage("libcore.java.lang");
+        assertEquals("package libcore.java.lang, Unknown, version 0.0", libcoreJavaLang.toString());
     }
 
     // http://b/5171136
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java
index a694981..0e40644 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java
@@ -15,8 +15,6 @@
  */
 package libcore.java.lang.reflect.annotations;
 
-import junit.framework.TestCase;
-
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -24,12 +22,19 @@
 import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
 import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
 
-import dalvik.system.VMRuntime;
+import libcore.junit.junit3.TestCaseWithRules;
+import libcore.junit.util.SwitchTargetSdkVersionRule;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
 
 /**
  * Tests for the behavior of Annotation instances at runtime.
  */
-public class AnnotationsTest extends TestCase {
+public class AnnotationsTest extends TestCaseWithRules {
+
+    @Rule
+    public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
 
     enum Breakfast { WAFFLES, PANCAKES }
 
@@ -88,24 +93,25 @@
     @ClassRetentionAnnotation @RuntimeRetentionAnnotation @SourceRetentionAnnotation
     public static class RetentionAnnotations {}
 
-    public void testRetentionPolicy() {
-        // b/29500035
-        int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
-        try {
-            // Test N and later behavior
-            VMRuntime.getRuntime().setTargetSdkVersion(24);
-            Annotation classRetentionAnnotation =
+    // b/29500035
+    @TargetSdkVersion(23)
+    public void testRetentionPolicy_targetSdkVersion_23() {
+        // Test pre-N behavior
+        Annotation classRetentionAnnotation =
                 RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class);
-            assertNull(classRetentionAnnotation);
+        assertNotNull(classRetentionAnnotation);
+    }
 
-            // Test pre-N behavior
-            VMRuntime.getRuntime().setTargetSdkVersion(23);
-            classRetentionAnnotation =
-                RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class);
-            assertNotNull(classRetentionAnnotation);
-        } finally {
-            VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion);
-        }
+    // b/29500035
+    @TargetSdkVersion(24)
+    public void testRetentionPolicy_targetSdkVersion_24() {
+        // Test N and later behavior
+        Annotation classRetentionAnnotation =
+            RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class);
+        assertNull(classRetentionAnnotation);
+    }
+
+    public void testRetentionPolicy() {
         assertNotNull(RetentionAnnotations.class.getAnnotation(RuntimeRetentionAnnotation.class));
         assertNull(RetentionAnnotations.class.getAnnotation(SourceRetentionAnnotation.class));
     }
diff --git a/luni/src/test/java/libcore/java/util/CollectionsTest.java b/luni/src/test/java/libcore/java/util/CollectionsTest.java
index 8ca3122..c92bcd4 100644
--- a/luni/src/test/java/libcore/java/util/CollectionsTest.java
+++ b/luni/src/test/java/libcore/java/util/CollectionsTest.java
@@ -47,9 +47,12 @@
 import java.util.concurrent.LinkedBlockingDeque;
 
 import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
 
-import dalvik.system.VMRuntime;
+import libcore.junit.junit3.TestCaseWithRules;
+import libcore.junit.util.SwitchTargetSdkVersionRule;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
 
 import static java.util.Collections.checkedNavigableMap;
 import static java.util.Collections.checkedQueue;
@@ -61,7 +64,10 @@
 import static java.util.Spliterator.SUBSIZED;
 import static libcore.java.util.SpliteratorTester.assertHasCharacteristics;
 
-public final class CollectionsTest extends TestCase {
+public final class CollectionsTest extends TestCaseWithRules {
+
+    @Rule
+    public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
 
     private static final Object NOT_A_STRING = new Object();
     private static final Object A_STRING = "string";
@@ -156,86 +162,55 @@
      * Tests that when targetSdk {@code <= 25}, Collections.sort() does not delegate
      * to List.sort().
      */
+    @TargetSdkVersion(25)
     public void testSort_nougatOrEarlier_doesNotDelegateToListSort() {
-        runOnTargetSdk(25, () -> { // Nougat MR1 / MR2
-            ArrayListInheritor<String> list = new ArrayListInheritor<>(
-                    Arrays.asList("a", "c", "b"));
-            assertEquals(0, list.numSortCalls());
-            Collections.sort(list);
-            assertEquals(0, list.numSortCalls());
-        });
+        // Nougat MR1 / MR2
+        ArrayListInheritor<String> list = new ArrayListInheritor<>(Arrays.asList("a", "c", "b"));
+        assertEquals(0, list.numSortCalls());
+        Collections.sort(list);
+        assertEquals(0, list.numSortCalls());
     }
 
+    @TargetSdkVersion(26)
     public void testSort_postNougat_delegatesToListSort() {
-        runOnTargetSdkAtLeast(26, () -> {
-            ArrayListInheritor<String> list = new ArrayListInheritor<>(
-                    Arrays.asList("a", "c", "b"));
-            assertEquals(0, list.numSortCalls());
-            Collections.sort(list);
-            assertEquals(1, list.numSortCalls());
-        });
+        ArrayListInheritor<String> list = new ArrayListInheritor<>(Arrays.asList("a", "c", "b"));
+        assertEquals(0, list.numSortCalls());
+        Collections.sort(list);
+        assertEquals(1, list.numSortCalls());
     }
 
+    @TargetSdkVersion(26)
     public void testSort_modcountUnmodifiedForLinkedList() {
-        runOnTargetSdkAtLeast(26, () -> {
-            LinkedList<String> list = new LinkedList<>(Arrays.asList(
-                    "red", "green", "blue", "violet"));
-            Iterator<String> it = list.iterator();
-            it.next();
-            Collections.sort(list);
-            it.next(); // does not throw ConcurrentModificationException
-        });
+        // does not throw ConcurrentModificationException
+        LinkedList<String> list = new LinkedList<>(Arrays.asList("red", "green", "blue", "violet"));
+        Iterator<String> it = list.iterator();
+        it.next();
+        Collections.sort(list);
+        it.next(); // does not throw ConcurrentModificationException
     }
 
+    @TargetSdkVersion(26)
     public void testSort_modcountModifiedForArrayListAndSubclasses() {
-        runOnTargetSdkAtLeast(26, () -> {
-            List<String> testData = Arrays.asList("red", "green", "blue", "violet");
+        List<String> testData = Arrays.asList("red", "green", "blue", "violet");
 
-            ArrayList<String> list = new ArrayList<>(testData);
-            Iterator<String> it = list.iterator();
-            it.next();
-            Collections.sort(list);
-            try {
-                it.next();
-                fail();
-            } catch (ConcurrentModificationException expected) {
-            }
-
-            list = new ArrayListInheritor<>(testData);
-            it = list.iterator();
-            it.next();
-            Collections.sort(list);
-            try {
-                it.next();
-                fail();
-            } catch (ConcurrentModificationException expected) {
-            }
-        });
-    }
-
-    /**
-     * Runs the given runnable on this thread with the targetSdkVersion temporarily set
-     * to the specified value, unless the current value is already higher.
-     */
-    private static void runOnTargetSdkAtLeast(int minimumTargetSdkForTest, Runnable runnable) {
-        int targetSdkForTest = Math.max(minimumTargetSdkForTest,
-                VMRuntime.getRuntime().getTargetSdkVersion());
-        runOnTargetSdk(targetSdkForTest, runnable);
-    }
-
-    /**
-     * Runs the given runnable on this thread with the targetSdkVersion temporarily set
-     * to the specified value. This helps test behavior that depends on an API level
-     * other than the current one (e.g. between releases).
-     */
-    private static void runOnTargetSdk(int targetSdkForTest, Runnable runnable) {
-        VMRuntime runtime = VMRuntime.getRuntime();
-        int targetSdk = runtime.getTargetSdkVersion();
+        ArrayList<String> list = new ArrayList<>(testData);
+        Iterator<String> it = list.iterator();
+        it.next();
+        Collections.sort(list);
         try {
-            runtime.setTargetSdkVersion(targetSdkForTest);
-            runnable.run();
-        } finally {
-            runtime.setTargetSdkVersion(targetSdk);
+            it.next();
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+
+        list = new ArrayListInheritor<>(testData);
+        it = list.iterator();
+        it.next();
+        Collections.sort(list);
+        try {
+            it.next();
+            fail();
+        } catch (ConcurrentModificationException expected) {
         }
     }
 
diff --git a/luni/src/test/java/libcore/libcore/io/OsTest.java b/luni/src/test/java/libcore/libcore/io/OsTest.java
index 91105bf..557b640 100644
--- a/luni/src/test/java/libcore/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/libcore/io/OsTest.java
@@ -309,13 +309,67 @@
     Libcore.os.close(clientFd);
   }
 
+  interface ExceptionalRunnable {
+    public void run() throws Exception;
+  }
+
+  /**
+   * Expects that the given Runnable will throw an exception of the specified class. If the class is
+   * ErrnoException, and expectedErrno is non-null, also checks that the errno is equal to
+   * expectedErrno.
+   */
+  private static void expectException(ExceptionalRunnable r, Class<? extends Exception> exClass,
+        Integer expectedErrno, String msg) {
+    try {
+      r.run();
+      fail(msg + " did not throw exception");
+    } catch (Exception e) {
+      assertEquals(msg + " threw unexpected exception", exClass, e.getClass());
+
+      if (expectedErrno != null) {
+        if (e instanceof ErrnoException) {
+          assertEquals(msg + "threw ErrnoException with unexpected error number",
+              (int) expectedErrno, ((ErrnoException) e).errno);
+        } else {
+          fail("Can only pass expectedErrno when expecting ErrnoException");
+        }
+      }
+
+    }
+  }
+
+  private static void expectBindException(FileDescriptor socket, SocketAddress addr,
+      Class exClass, Integer expectedErrno) {
+    String msg = String.format("bind(%s, %s)", socket, addr);
+    expectException(() -> { Libcore.os.bind(socket, addr); }, exClass, expectedErrno, msg);
+  }
+
+  private static void expectConnectException(FileDescriptor socket, SocketAddress addr,
+      Class exClass, Integer expectedErrno) {
+    String msg = String.format("connect(%s, %s)", socket, addr);
+    expectException(() -> { Libcore.os.connect(socket, addr); }, exClass, expectedErrno, msg);
+  }
+
+  private static void expectSendtoException(FileDescriptor socket, SocketAddress addr,
+      Class exClass, Integer expectedErrno) {
+    String msg = String.format("sendto(%s, %s)", socket, addr);
+    byte[] packet = new byte[42];
+    expectException(() -> { Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr); },
+        exClass, expectedErrno, msg);
+  }
+
   private static void expectBindConnectSendtoSuccess(FileDescriptor socket, String socketDesc,
                                                      SocketAddress addr) {
     String msg = socketDesc + " socket to " + addr.toString();
 
     try {
-      // Expect bind to succeed.
       try {
+        // Expect that bind throws when any of its arguments are null.
+        expectBindException(null, addr, ErrnoException.class, EBADF);
+        expectBindException(socket, null, NullPointerException.class, null);
+        expectBindException(null, null, NullPointerException.class, null);
+
+        // Expect bind to succeed.
         Libcore.os.bind(socket, addr);
 
         // Find out which port we're actually bound to, and use that in subsequent connect() and
@@ -330,13 +384,26 @@
           addr = socknameISA;
         }
 
+        // Expect sendto with a null address to throw because the socket is not connected, but to
+        // succeed with a non-null address.
+        byte[] packet = new byte[42];
+        Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr);
+        // UNIX and IP sockets return different errors for this operation, so we can't check errno.
+        expectSendtoException(socket, null, ErrnoException.class, null);
+        expectSendtoException(null, null, ErrnoException.class, EBADF);
+
+        // Expect that connect throws when any of its arguments are null.
+        expectConnectException(null, addr, ErrnoException.class, EBADF);
+        expectConnectException(socket, null, NullPointerException.class, null);
+        expectConnectException(null, null, NullPointerException.class, null);
+
         // Expect connect to succeed.
         Libcore.os.connect(socket, addr);
         assertEquals(Libcore.os.getsockname(socket), Libcore.os.getpeername(socket));
 
-        // Expect sendto to succeed.
-        byte[] packet = new byte[42];
+        // Expect sendto to succeed both when given an explicit address and a null address.
         Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr);
+        Libcore.os.sendto(socket, packet, 0, packet.length, 0, null);
       } catch (SocketException | ErrnoException e) {
         fail("Expected success for " + msg + ", but got: " + e);
       }
diff --git a/luni/src/test/java/libcore/libcore/timezone/TimeZoneDataFilesTest.java b/luni/src/test/java/libcore/libcore/timezone/TimeZoneDataFilesTest.java
index a8e0acd..e94b96e 100644
--- a/luni/src/test/java/libcore/libcore/timezone/TimeZoneDataFilesTest.java
+++ b/luni/src/test/java/libcore/libcore/timezone/TimeZoneDataFilesTest.java
@@ -21,10 +21,21 @@
 import libcore.timezone.TimeZoneDataFiles;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 public class TimeZoneDataFilesTest {
 
+    private static final String ANDROID_TZDATA_ROOT_ENV = "ANDROID_TZDATA_ROOT";
+    private static final String ANDROID_RUNTIME_ROOT_ENV = "ANDROID_RUNTIME_ROOT";
+
+    @Test
+    public void expectedEnvironmentVariables() {
+        // These environment variables are required to locate data files used by libcore / ICU.
+        assertNotNull(System.getenv(ANDROID_TZDATA_ROOT_ENV));
+        assertNotNull(System.getenv(ANDROID_RUNTIME_ROOT_ENV));
+    }
+
     @Test
     public void getTimeZoneFilePaths() {
         String[] paths = TimeZoneDataFiles.getTimeZoneFilePaths("foo");
@@ -33,10 +44,10 @@
         assertTrue(paths[0].contains("/misc/zoneinfo/current/"));
         assertTrue(paths[0].endsWith("/foo"));
 
-        assertTrue(paths[1].startsWith("/apex/com.android.tzdata/"));
+        assertTrue(paths[1].startsWith(System.getenv(ANDROID_TZDATA_ROOT_ENV)));
         assertTrue(paths[1].endsWith("/foo"));
 
-        assertTrue(paths[2].startsWith("/apex/com.android.runtime/"));
+        assertTrue(paths[2].startsWith(System.getenv(ANDROID_RUNTIME_ROOT_ENV)));
         assertTrue(paths[2].endsWith("/foo"));
 
         assertTrue(paths[3].contains("/usr/share/zoneinfo/"));
@@ -52,8 +63,16 @@
         String[] paths = icuDataPath.split(":");
         assertEquals(3, paths.length);
 
-        assertTrue(paths[0].contains("/misc/zoneinfo/current/icu"));
-        assertTrue(paths[1].startsWith("/apex/com.android.tzdata"));
-        assertTrue(paths[2].contains("/etc/icu"));
+        String dataDirPath = paths[0];
+        assertTrue(dataDirPath + " invalid", dataDirPath.contains("/misc/zoneinfo/current/icu"));
+
+        String tzdataModulePath = paths[1];
+        assertTrue(tzdataModulePath + " invalid",
+                tzdataModulePath.startsWith(System.getenv(ANDROID_TZDATA_ROOT_ENV)));
+
+        String runtimeModulePath = paths[2];
+        assertTrue(runtimeModulePath + " invalid",
+                runtimeModulePath.startsWith(System.getenv(ANDROID_RUNTIME_ROOT_ENV)));
+        assertTrue(runtimeModulePath + " invalid", runtimeModulePath.contains("/etc/icu"));
     }
 }
diff --git a/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java b/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java
index 5d90dd3..fe454db 100644
--- a/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java
+++ b/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java
@@ -18,21 +18,25 @@
 package org.apache.harmony.regex.tests.java.util.regex;
 
 import java.io.Serializable;
-import java.util.Arrays;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
 
-import junit.framework.TestCase;
-
+import libcore.junit.junit3.TestCaseWithRules;
+import libcore.junit.util.SwitchTargetSdkVersionRule;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
 import org.apache.harmony.testframework.serialization.SerializationTest;
 import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert;
 
-import dalvik.system.VMRuntime;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
 
 import static java.util.Arrays.asList;
 
-public class PatternTest extends TestCase {
+public class PatternTest extends TestCaseWithRules {
+    @Rule
+    public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
+
     String[] testPatterns = {
             "(a|b)*abb",
             "(1*2*3*4*)*567",
@@ -203,13 +207,12 @@
         assertEquals(asList(""), asList("".split("", -1)));
     }
 
+    @TargetSdkVersion(28)
     public void testSplitOnEmptyPattern_api28() {
-        runWithTargetSdkVersion(28, () -> {
-            assertEquals(asList("", "t", "e", "s", "t"), asList("test".split("")));
-            assertEquals(asList(""), asList("".split("")));
-            assertEquals(asList(""), asList(Pattern.compile("").split("")));
-            assertEquals(asList(""), asList("".split("", -1)));
-        });
+        assertEquals(asList("", "t", "e", "s", "t"), asList("test".split("")));
+        assertEquals(asList(""), asList("".split("")));
+        assertEquals(asList(""), asList(Pattern.compile("").split("")));
+        assertEquals(asList(""), asList("".split("", -1)));
     }
 
     /**
@@ -229,30 +232,18 @@
         assertEquals(asList("aar", "dvark"), asList("aardvark".split("(?=d)")));
     }
 
+    @TargetSdkVersion(28)
     public void testMatchBeginningOfInputSequence_api28() {
-        runWithTargetSdkVersion(28, () -> {
-            // Positive-width match at the beginning of the input.
-            assertEquals(asList("", "", "rdv", "rk"), asList("aardvark".split("a")));
-            assertEquals(asList("", "anana"), asList("banana".split("b")));
-            // Zero-width match at the beginning of the input
-            assertEquals(asList("", "a", "ardv", "ark"), asList("aardvark".split("(?=a)")));
-            assertEquals(asList("banana"), asList("banana".split("(?=b)")));
+        // Positive-width match at the beginning of the input.
+        assertEquals(asList("", "", "rdv", "rk"), asList("aardvark".split("a")));
+        assertEquals(asList("", "anana"), asList("banana".split("b")));
+        // Zero-width match at the beginning of the input
+        assertEquals(asList("", "a", "ardv", "ark"), asList("aardvark".split("(?=a)")));
+        assertEquals(asList("banana"), asList("banana".split("(?=b)")));
 
-            // For comparison, matches in the middle of the input never yield an empty substring:
-            assertEquals(asList("aar", "vark"), asList("aardvark".split("d")));
-            assertEquals(asList("aar", "dvark"), asList("aardvark".split("(?=d)")));
-        });
-    }
-
-    private static void runWithTargetSdkVersion(int targetSdkVersion, Runnable runnable) {
-        VMRuntime vmRuntime = VMRuntime.getRuntime();
-        int oldVersion = vmRuntime.getTargetSdkVersion();
-        vmRuntime.setTargetSdkVersion(targetSdkVersion);
-        try {
-            runnable.run();
-        } finally {
-            vmRuntime.setTargetSdkVersion(oldVersion);
-        }
+        // For comparison, matches in the middle of the input never yield an empty substring:
+        assertEquals(asList("aar", "vark"), asList("aardvark".split("d")));
+        assertEquals(asList("aar", "dvark"), asList("aardvark".split("(?=d)")));
     }
 
     public void testPattern() {
diff --git a/ojluni/src/main/java/java/io/ObjectStreamClass.java b/ojluni/src/main/java/java/io/ObjectStreamClass.java
index 4a183f8..7561aa8 100644
--- a/ojluni/src/main/java/java/io/ObjectStreamClass.java
+++ b/ojluni/src/main/java/java/io/ObjectStreamClass.java
@@ -1798,9 +1798,14 @@
             }
 
             // Android-changed: Clinit serialization workaround b/29064453
-            boolean checkSuperclass = !(VMRuntime.getRuntime().getTargetSdkVersion()
-                                       <= MAX_SDK_TARGET_FOR_CLINIT_UIDGEN_WORKAROUND);
-            if (hasStaticInitializer(cl, checkSuperclass)) {
+            // Prior to SDK 24 hasStaticInitializer() would return true if the superclass had a
+            // static initializer, that was contrary to the specification. In SDK 24 the default
+            // behavior was corrected but the old behavior was preserved for apps that targeted 23
+            // or below in order to maintain backwards compatibility.
+            boolean inheritStaticInitializer =
+                (VMRuntime.getRuntime().getTargetSdkVersion()
+                <= MAX_SDK_TARGET_FOR_CLINIT_UIDGEN_WORKAROUND);
+            if (hasStaticInitializer(cl, inheritStaticInitializer)) {
                 dout.writeUTF("<clinit>");
                 dout.writeInt(Modifier.STATIC);
                 dout.writeUTF("()V");
@@ -1880,10 +1885,14 @@
     /**
      * Returns true if the given class defines a static initializer method,
      * false otherwise.
-     * if checkSuperclass is false, we use a buggy version (for compatibility reason) that
-     * will return true even if only the superclass has a static initializer method.
+     *
+     * @param inheritStaticInitializer if false then this method will return true iff the given
+     * class has its own static initializer, if true (used for backwards compatibility for apps
+     * that target SDK version <= {@link #MAX_SDK_TARGET_FOR_CLINIT_UIDGEN_WORKAROUND}) it will
+     * return true if the given class or any of its ancestor classes have a static initializer.
      */
-    private native static boolean hasStaticInitializer(Class<?> cl, boolean checkSuperclass);
+    private native static boolean hasStaticInitializer(
+        Class<?> cl, boolean inheritStaticInitializer);
     // END Android-changed: Clinit serialization workaround b/29064453
 
     /**
diff --git a/ojluni/src/main/java/java/util/Locale.java b/ojluni/src/main/java/java/util/Locale.java
index 6af037ce..3908fa3 100644
--- a/ojluni/src/main/java/java/util/Locale.java
+++ b/ojluni/src/main/java/java/util/Locale.java
@@ -524,6 +524,10 @@
  *     <td><a href="http://site.icu-project.org/download/60">ICU 60.2</a></td>
  *     <td><a href="http://cldr.unicode.org/index/downloads/cldr-32">CLDR 32.0.1</a></td>
  *     <td><a href="http://www.unicode.org/versions/Unicode10.0.0/">Unicode 10.0</a></td></tr>
+ * <tr><td>Android Q</td>
+ *     <td><a href="http://site.icu-project.org/download/63">ICU 63.1</a></td>
+ *     <td><a href="http://cldr.unicode.org/index/downloads/cldr-34">CLDR 34</a></td>
+ *     <td><a href="http://www.unicode.org/versions/Unicode11.0.0/">Unicode 11.0</a></td></tr>
  * </table>
  *
  * <a name="default_locale"></a><h4>Be wary of the default locale</h3>
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLEngine.java b/ojluni/src/main/java/javax/net/ssl/SSLEngine.java
index d5070ac..76f8f66 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLEngine.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLEngine.java
@@ -1523,6 +1523,7 @@
     public abstract boolean isOutboundDone();
 
 
+    // Android-changed: Added warnings about misuse
     /**
      * Returns the names of the cipher suites which could be enabled for use
      * on this engine.  Normally, only a subset of these will actually
@@ -1530,6 +1531,15 @@
      * do not meet quality of service requirements for those defaults.  Such
      * cipher suites might be useful in specialized applications.
      *
+     * <p class="caution">Applications should not blindly enable all supported
+     * cipher suites.  The supported cipher suites can include signaling cipher suite
+     * values that can cause connection problems if enabled inappropriately.
+     *
+     * <p>The proper way to use this method is to either check if a specific cipher
+     * suite is supported via {@code Arrays.asList(getSupportedCipherSuites()).contains(...)}
+     * or to filter a desired list of cipher suites to only the supported ones via
+     * {@code desiredSuiteSet.retainAll(Arrays.asList(getSupportedCipherSuites()))}.
+     *
      * @return  an array of cipher suite names
      * @see     #getEnabledCipherSuites()
      * @see     #setEnabledCipherSuites(String [])
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLServerSocket.java b/ojluni/src/main/java/javax/net/ssl/SSLServerSocket.java
index 7b07cfa..35c14ea 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLServerSocket.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLServerSocket.java
@@ -229,6 +229,7 @@
     public abstract void setEnabledCipherSuites(String suites []);
 
 
+    // Android-changed: Added warnings about misuse
     /**
      * Returns the names of the cipher suites which could be enabled for use
      * on an SSL connection.
@@ -238,6 +239,15 @@
      * do not meet quality of service requirements for those defaults.  Such
      * cipher suites are useful in specialized applications.
      *
+     * <p class="caution">Applications should not blindly enable all supported
+     * cipher suites.  The supported cipher suites can include signaling cipher suite
+     * values that can cause connection problems if enabled inappropriately.
+     *
+     * <p>The proper way to use this method is to either check if a specific cipher
+     * suite is supported via {@code Arrays.asList(getSupportedCipherSuites()).contains(...)}
+     * or to filter a desired list of cipher suites to only the supported ones via
+     * {@code desiredSuiteSet.retainAll(Arrays.asList(getSupportedCipherSuites()))}.
+     *
      * @return an array of cipher suite names
      * @see #getEnabledCipherSuites()
      * @see #setEnabledCipherSuites(String [])
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLServerSocketFactory.java b/ojluni/src/main/java/javax/net/ssl/SSLServerSocketFactory.java
index 2db63bb..5067f8f 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLServerSocketFactory.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLServerSocketFactory.java
@@ -162,6 +162,7 @@
     public abstract String [] getDefaultCipherSuites();
 
 
+    // Android-changed: Added warnings about misuse
     /**
      * Returns the names of the cipher suites which could be enabled for use
      * on an SSL connection created by this factory.
@@ -170,6 +171,15 @@
      * do not meet quality of service requirements for those defaults.  Such
      * cipher suites are useful in specialized applications.
      *
+     * <p class="caution">Applications should not blindly enable all supported
+     * cipher suites.  The supported cipher suites can include signaling cipher suite
+     * values that can cause connection problems if enabled inappropriately.
+     *
+     * <p>The proper way to use this method is to either check if a specific cipher
+     * suite is supported via {@code Arrays.asList(getSupportedCipherSuites()).contains(...)}
+     * or to filter a desired list of cipher suites to only the supported ones via
+     * {@code desiredSuiteSet.retainAll(Arrays.asList(getSupportedCipherSuites()))}.
+     *
      * @return an array of cipher suite names
      * @see #getDefaultCipherSuites()
      */
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLSocket.java b/ojluni/src/main/java/javax/net/ssl/SSLSocket.java
index f75538e..a2ba960 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLSocket.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLSocket.java
@@ -1019,6 +1019,7 @@
         { super(address, port, clientAddress, clientPort); }
 
 
+    // Android-changed: Added warnings about misuse
     /**
      * Returns the names of the cipher suites which could be enabled for use
      * on this connection.  Normally, only a subset of these will actually
@@ -1026,6 +1027,15 @@
      * do not meet quality of service requirements for those defaults.  Such
      * cipher suites might be useful in specialized applications.
      *
+     * <p class="caution">Applications should not blindly enable all supported
+     * cipher suites.  The supported cipher suites can include signaling cipher suite
+     * values that can cause connection problems if enabled inappropriately.
+     *
+     * <p>The proper way to use this method is to either check if a specific cipher
+     * suite is supported via {@code Arrays.asList(getSupportedCipherSuites()).contains(...)}
+     * or to filter a desired list of cipher suites to only the supported ones via
+     * {@code desiredSuiteSet.retainAll(Arrays.asList(getSupportedCipherSuites()))}.
+     *
      * @return an array of cipher suite names
      * @see #getEnabledCipherSuites()
      * @see #setEnabledCipherSuites(String [])
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLSocketFactory.java b/ojluni/src/main/java/javax/net/ssl/SSLSocketFactory.java
index 93b5dc7..8b0966b 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLSocketFactory.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLSocketFactory.java
@@ -187,6 +187,7 @@
      */
     public abstract String [] getDefaultCipherSuites();
 
+    // Android-changed: Added warnings about misuse
     /**
      * Returns the names of the cipher suites which could be enabled for use
      * on an SSL connection.  Normally, only a subset of these will actually
@@ -194,6 +195,15 @@
      * do not meet quality of service requirements for those defaults.  Such
      * cipher suites are useful in specialized applications.
      *
+     * <p class="caution">Applications should not blindly enable all supported
+     * cipher suites.  The supported cipher suites can include signaling cipher suite
+     * values that can cause connection problems if enabled inappropriately.
+     *
+     * <p>The proper way to use this method is to either check if a specific cipher
+     * suite is supported via {@code Arrays.asList(getSupportedCipherSuites()).contains(...)}
+     * or to filter a desired list of cipher suites to only the supported ones via
+     * {@code desiredSuiteSet.retainAll(Arrays.asList(getSupportedCipherSuites()))}.
+     *
      * @see #getDefaultCipherSuites()
      * @return an array of cipher suite names
      */
diff --git a/ojluni/src/main/native/ObjectStreamClass.c b/ojluni/src/main/native/ObjectStreamClass.c
index 95cd4f0..3d3fdef 100644
--- a/ojluni/src/main/native/ObjectStreamClass.c
+++ b/ojluni/src/main/native/ObjectStreamClass.c
@@ -51,12 +51,19 @@
  * otherwise.
  */
 JNIEXPORT jboolean JNICALL
+// Android-changed: Added inheritStaticInitializer parameter.
+// The inheritStaticInitializer parameter is set to JNI_TRUE when behavior compatible with
+// Android version 23 is required.
 ObjectStreamClass_hasStaticInitializer(JNIEnv *env, jclass this,
                                        jclass clazz,
-                                       jboolean checkSuperclass)
+                                       jboolean inheritStaticInitializer)
 {
     jclass superCl = NULL;
     jmethodID superClinitId = NULL;
+
+    // Android-changed: Added comment to explain behavior.
+    // Search for a static initializer method in this class and up through its
+    // ancestor super classes, returning the id of the first method found.
     jmethodID clinitId =
         (*env)->GetStaticMethodID(env, clazz, "<clinit>", "()V");
     if (clinitId == NULL) {     /* error thrown */
@@ -68,13 +75,15 @@
         return JNI_FALSE;
     }
 
-    // Android-changed, if checkSuperclass == true, remove check for
-    // superclass clinitId != child clinitId.
-    // We're returning true to enable deserializing classes without explicit serialVersionID
-    // that would fail in this check (b/29064453).
-    if (checkSuperclass == JNI_FALSE) {
+    // BEGIN Android-changed: Exit immediately if version 23 behavior is required.
+    // At this point the class or one of its ancestor super classes has a
+    // static initializer. If inheritStaticInitializer is true then this method
+    // can simply return true. Otherwise, it needs to check that the static
+    // initializer was not inherited (b/29064453).
+    if (inheritStaticInitializer == JNI_TRUE) {
         return JNI_TRUE;
     }
+    // END Android-changed: Exit immediately if version 23 behavior is required.
 
     /*
      * Check superclass for static initializer as well--if the same method ID
@@ -83,6 +92,7 @@
      * JNI spec makes no guarantee that GetStaticMethodID will not return the
      * ID for a superclass initializer.
      */
+
     if ((superCl = (*env)->GetSuperclass(env, clazz)) == NULL) {
         return JNI_TRUE;
     }
diff --git a/test-rules/src/main/java/libcore/junit/util/SwitchTargetSdkVersionRule.java b/test-rules/src/main/java/libcore/junit/util/SwitchTargetSdkVersionRule.java
new file mode 100644
index 0000000..455d3cb
--- /dev/null
+++ b/test-rules/src/main/java/libcore/junit/util/SwitchTargetSdkVersionRule.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2019 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.junit.util;
+
+import static org.junit.Assert.fail;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Allows tests to specify the target SDK version that they want to run as.
+ *
+ * <p>To use add the following to the test class. It will only change the behavior of a test method
+ * if it is annotated with {@link TargetSdkVersion}.
+ *
+ * <pre>
+ * &#64;Rule
+ * public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
+ * </pre>
+ *
+ * <p>Each test method that needs to run with a specific target SDK version needs to be annotated
+ * with {@link TargetSdkVersion} specifying the required SDK version. e.g.
+ *
+ * <pre>
+ *   &#64;Test
+ *   &#64;TargetSdkVersion(23)
+ *   public void testAsIfTargetedAtSDK23() {
+ *     assertEquals(23, VMRuntime.getRuntime().getTargetSdkVersion());
+ *   }
+ * </pre>
+ *
+ * <p>Within the body of the method the {@code VMRuntime.getTargetSdkVersion()} will be set to the
+ * value specified in the annotation.
+ *
+ * <p>If used on a platform that does not support the {@code dalvik.system.VMRuntime} class then any
+ * test annotated with {@link TargetSdkVersion} will fail with a message explaining that it is not
+ * supported.
+ */
+public abstract class SwitchTargetSdkVersionRule implements TestRule {
+
+  private static final TestRule INSTANCE;
+
+  private static final String VMRUNTIME = "dalvik.system.VMRuntime";
+
+  // Use reflection so that this rule can compile and run against RI core libraries.
+  static {
+    TestRule rule;
+    try {
+      // Assume that VMRuntime is supported and create a rule instance that will use it.
+      rule = new SwitchVMRuntimeUsingReflection();
+    } catch (ClassNotFoundException | NoSuchMethodException e) {
+      // VMRuntime is not supported.
+      rule = new VMRuntimeNotSupported();
+    }
+
+    INSTANCE = rule;
+  }
+
+  public static TestRule getInstance() {
+    return INSTANCE;
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target(ElementType.METHOD)
+  public @interface TargetSdkVersion {
+    int value();
+  }
+
+  private SwitchTargetSdkVersionRule() {
+  }
+
+  @Override
+  public Statement apply(final Statement statement, Description description) {
+    TargetSdkVersion targetSdkVersion = description.getAnnotation(TargetSdkVersion.class);
+    if (targetSdkVersion == null) {
+      return statement;
+    }
+
+    return createStatement(statement, targetSdkVersion.value());
+  }
+
+  /**
+   * Create the {@link Statement} that will be run for the specific {@code targetSdkVersion}.
+   *
+   * @param statement the {@link Statement} encapsulating the test method.
+   * @param targetSdkVersion the target SDK version to use within the body of the test.
+   * @return the created {@link Statement}.
+   */
+  protected abstract Statement createStatement(Statement statement, int targetSdkVersion);
+
+  /**
+   * Switch the value of {@code VMRuntime.getTargetSdkVersion()} for tests annotated with
+   * {@link TargetSdkVersion}.
+   *
+   * <p>Uses reflection so this class can compile and run on OpenJDK.
+   */
+  private static class SwitchVMRuntimeUsingReflection extends SwitchTargetSdkVersionRule {
+
+    private final Method runtimeInstanceGetter;
+    private final Method targetSdkVersionGetter;
+    private final Method targetSdkVersionSetter;
+
+    private SwitchVMRuntimeUsingReflection() throws ClassNotFoundException, NoSuchMethodException {
+      ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+      Class<?> runtimeClass = classLoader.loadClass(VMRUNTIME);
+
+      this.runtimeInstanceGetter = runtimeClass.getMethod("getRuntime");
+      this.targetSdkVersionGetter = runtimeClass.getMethod("getTargetSdkVersion");
+      this.targetSdkVersionSetter = runtimeClass.getMethod("setTargetSdkVersion", int.class);
+    }
+
+    @Override
+    protected Statement createStatement(Statement statement, int targetSdkVersion) {
+      return new Statement() {
+        @Override
+        public void evaluate() throws Throwable {
+          Object runtime = runtimeInstanceGetter.invoke(null);
+          int oldTargetSdkVersion = (int) targetSdkVersionGetter.invoke(runtime);
+          try {
+            targetSdkVersionSetter.invoke(runtime, targetSdkVersion);
+            statement.evaluate();
+          } finally {
+            targetSdkVersionSetter.invoke(runtime, oldTargetSdkVersion);
+          }
+        }
+      };
+    }
+  }
+
+  /**
+   * VMRuntime is not supported on this platform so fail all tests that target a specific SDK
+   * version
+   */
+  private static class VMRuntimeNotSupported extends SwitchTargetSdkVersionRule {
+
+    @Override
+    protected Statement createStatement(Statement statement, int targetSdkVersion) {
+      return new Statement() {
+        @Override
+        public void evaluate() {
+          fail("Targeting SDK version not supported as " + VMRUNTIME + " is not supported");
+        }
+      };
+    }
+  }
+}
diff --git a/test-rules/src/test/java/libcore/junit/util/SwitchTargetSdkVersionRuleTest.java b/test-rules/src/test/java/libcore/junit/util/SwitchTargetSdkVersionRuleTest.java
new file mode 100644
index 0000000..ca15a1e
--- /dev/null
+++ b/test-rules/src/test/java/libcore/junit/util/SwitchTargetSdkVersionRuleTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.junit.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import dalvik.system.VMRuntime;
+import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link SwitchTargetSdkVersionRule}.
+ */
+@RunWith(JUnit4.class)
+public class SwitchTargetSdkVersionRuleTest {
+
+  @Rule
+  public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
+
+  @Test
+  @TargetSdkVersion(23)
+  public void testRunningAsIfTargetedAtSDKVersion23() {
+    assertEquals(23, VMRuntime.getRuntime().getTargetSdkVersion());
+  }
+
+  @Test
+  public void testRunningAsIfTargetedAtCurrentSDKVersion() {
+    assertNotEquals(23, VMRuntime.getRuntime().getTargetSdkVersion());
+  }
+}