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>
+ * @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>
+ * @Test
+ * @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());
+ }
+}