Merge "Tests for java.security.cert.Certificate(PublicKey, Provider)"
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index ff0ae37..7569737 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -87,6 +87,7 @@
LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) $(android_icu4j_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-all
@@ -101,6 +102,7 @@
LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-oj
@@ -117,6 +119,7 @@
LOCAL_JAVA_RESOURCE_DIRS := $(android_icu4j_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-libart
@@ -138,6 +141,7 @@
LOCAL_SRC_FILES := $(openjdk_lambda_stub_files) $(openjdk_lambda_duplicate_stub_files)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-lambda-stubs
@@ -156,6 +160,7 @@
LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_DEX_PREOPT := false
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
@@ -190,6 +195,7 @@
LOCAL_JAVA_RESOURCE_DIRS := $(android_icu4j_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_DEX_PREOPT := false
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
@@ -262,6 +268,7 @@
LOCAL_JAVA_LIBRARIES := core-oj core-libart okhttp bouncycastle
LOCAL_STATIC_JAVA_LIBRARIES := testng
LOCAL_JAVACFLAGS := $(local_javac_flags)
+ LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-ojtests
@@ -286,6 +293,7 @@
okhttp \
testng
LOCAL_JAVACFLAGS := $(local_javac_flags)
+ LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-ojtests-public
@@ -312,6 +320,7 @@
LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-all-hostdex
@@ -326,6 +335,7 @@
LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-oj-hostdex
@@ -342,6 +352,7 @@
LOCAL_JAVA_RESOURCE_DIRS := $(android_icu4j_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-libart-hostdex
@@ -356,6 +367,7 @@
LOCAL_SRC_FILES := $(openjdk_lambda_stub_files) $(openjdk_lambda_duplicate_stub_files)
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-lambda-stubs-hostdex
@@ -428,6 +440,7 @@
okhttp-hostdex
LOCAL_STATIC_JAVA_LIBRARIES := testng-hostdex
LOCAL_JAVACFLAGS := $(local_javac_flags)
+ LOCAL_DX_FLAGS := --core-library
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_MODULE := core-ojtests-hostdex
diff --git a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
index 29a7a89..7ce09c9 100644
--- a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
+++ b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
@@ -247,6 +247,8 @@
int OP_USHR_INT_LIT8 = 0x00e2;
int OP_INVOKE_POLYMORPHIC = 0x00fa;
int OP_INVOKE_POLYMORPHIC_RANGE = 0x00fb;
+ int OP_INVOKE_CUSTOM = 0x00fc;
+ int OP_INVOKE_CUSTOM_RANGE = 0x00fd;
// END(libcore-opcodes)
/** Never implemented; do not use. */
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index 99d4d63..bab2f46 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -18,6 +18,7 @@
import java.io.File;
import java.net.URL;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@@ -68,6 +69,22 @@
}
}
+ /**
+ * Constructs an instance.
+ *
+ * dexFile must be an in-memory representation of a full dexFile.
+ *
+ * @param dexFiles the array of in-memory dex files containing classes.
+ * @param parent the parent class loader
+ *
+ * @hide
+ */
+ public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
+ // TODO We should support giving this a library search path maybe.
+ super(parent);
+ this.pathList = new DexPathList(this, dexFiles);
+ }
+
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 42cdac0..2a95450 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -21,7 +21,9 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import libcore.io.Libcore;
@@ -115,6 +117,12 @@
//System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
}
+ DexFile(ByteBuffer buf) throws IOException {
+ mCookie = openInMemoryDexFile(buf);
+ mInternalCookie = mCookie;
+ mFileName = null;
+ }
+
/**
* Opens a DEX file from a given filename, using a specified file
* to hold the optimized data.
@@ -230,7 +238,11 @@
}
@Override public String toString() {
- return getName();
+ if (mFileName != null) {
+ return getName();
+ } else {
+ return "InMemoryDexFile[cookie=" + Arrays.toString((long[]) mCookie) + "]";
+ }
}
/**
@@ -374,6 +386,17 @@
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 native Object createCookieWithDirectBuffer(ByteBuffer buf, int start, int end);
+ private static native Object createCookieWithArray(byte[] buf, int start, int end);
+
/*
* 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 444d483..b056d20 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -22,14 +22,15 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
+import libcore.io.ClassPathURLStreamHandler;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import libcore.io.ClassPathURLStreamHandler;
import static android.system.OsConstants.S_ISDIR;
@@ -75,6 +76,42 @@
private IOException[] dexElementsSuppressedExceptions;
/**
+ * Construct an instance.
+ *
+ * @param definingContext the context in which any as-yet unresolved
+ * classes should be defined
+ *
+ * @param dexFiles the bytebuffers containing the dex files that we should load classes from.
+ */
+ public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles) {
+ 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;
+ // TODO It might be useful to let in-memory dex-paths have native libraries.
+ this.nativeLibraryDirectories = Collections.emptyList();
+ this.systemNativeLibraryDirectories =
+ splitPaths(System.getProperty("java.library.path"), true);
+ this.nativeLibraryPathElements = makePathElements(this.systemNativeLibraryDirectories);
+
+ 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;
+ }
+ }
+
+ /**
* Constructs an instance.
*
* @param definingContext the context in which any as-yet unresolved
@@ -245,6 +282,25 @@
return result;
}
+ private 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);
+ elements[elementPos++] = new Element(dex);
+ } catch (IOException suppressed) {
+ System.logE("Unable to load dex file: " + buf, suppressed);
+ suppressedExceptions.add(suppressed);
+ }
+ }
+ if (elementPos != elements.length) {
+ elements = Arrays.copyOf(elements, elementPos);
+ }
+ return elements;
+ }
+
/**
* Makes an array of dex/resource path elements, one per element of
* the given array.
@@ -490,8 +546,8 @@
for (Element e : dexElements) {
String dexPath = e.getDexPath();
if (dexPath != null) {
- // Add the element to the list only if it is a file.
- // A null dex path signals the element is a resource directory.
+ // Add the element to the list only if it is a file. A null dex path signals the
+ // element is a resource directory or an in-memory dex file.
dexPaths.add(dexPath);
}
}
@@ -523,6 +579,11 @@
this.path = dexZipPath;
}
+ public Element(DexFile dexFile) {
+ this.dexFile = dexFile;
+ this.path = null;
+ }
+
public Element(File path) {
this.path = path;
this.dexFile = null;
diff --git a/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java b/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java
index b3c9f8f..0fa1e45 100644
--- a/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java
@@ -17,15 +17,24 @@
package dalvik.system;
import java.nio.ByteBuffer;
-import sun.misc.Cleaner;
/**
* A {@link ClassLoader} implementation that loads classes from a
* buffer containing a DEX file. This can be used to execute code that
* has not been written to the local file system.
*/
-public final class InMemoryDexClassLoader extends ClassLoader {
- private final DexData dexData;
+public final class InMemoryDexClassLoader extends BaseDexClassLoader {
+ /**
+ * Create an in-memory DEX class loader with the given dex buffers.
+ *
+ * @param dexBuffers array of buffers containing DEX files between
+ * <tt>buffer.position()</tt> and <tt>buffer.limit()</tt>.
+ * @param parent the parent class loader for delegation.
+ * @hide
+ */
+ public InMemoryDexClassLoader(ByteBuffer[] dexBuffers, ClassLoader parent) {
+ super(dexBuffers, parent);
+ }
/**
* Creates a new in-memory DEX class loader.
@@ -35,85 +44,6 @@
* @param parent the parent class loader for delegation.
*/
public InMemoryDexClassLoader(ByteBuffer dexBuffer, ClassLoader parent) {
- super(parent);
- if (dexBuffer == null) {
- throw new NullPointerException("dexData == null");
- }
- this.dexData = new DexData(dexBuffer);
- }
-
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- try {
- return dexData.findClass(name, this);
- } catch (Exception e) {
- throw new ClassNotFoundException("Didn't find class \"" + name + "\"", e);
- }
- }
-
- @Override
- protected synchronized Package getPackage(String name) {
- // This is duplicated from BaseDexClassLoader.getPackage which
- // has an extensive comment on why this needs to be defined.
- if (name == null || name.isEmpty()) {
- return null;
- }
-
- Package pkg = super.getPackage(name);
- if (pkg == null) {
- pkg = definePackage(name, "Unknown", "0.0", "Unknown",
- "Unknown", "0.0", "Unknown", null);
- }
- return pkg;
- }
-
- /**
- * Representation of native resources associated with DEX files loaded by
- * InMemoryDexClassLoader.
- */
- private static final class DexData {
- private final long cookie;
-
- DexData(ByteBuffer buffer) {
- if (buffer.isDirect()) {
- cookie = initializeWithDirectBuffer(buffer, buffer.position(), buffer.limit());
- } else {
- cookie = initializeWithArray(buffer.array(), buffer.position(), buffer.limit());
- }
-
- if (cookie != 0) {
- // Register for clean-up when this instance is phantom
- // reachable. Cleaner instances are inserted in a global
- // queue to preserve liveness. DexDataDeallocator must not
- // have a pointer to DexData otherwise it will prevent
- // cleaning and the freeing of the native resources.
- Cleaner.create(this, new DexDataDeallocator(cookie));
- }
- }
-
- protected Class<?> findClass(String name, ClassLoader loader) {
- return findClass(name, loader, cookie);
- }
-
- private static native long initializeWithDirectBuffer(ByteBuffer buffer,
- int start, int end);
- private static native long initializeWithArray(byte[] array, int start, int end);
- private static native void uninitialize(long cookie);
- private native Class findClass(String name, ClassLoader loader, long cookie);
- }
-
- /**
- * Helper class to release native resources associated with DexData instances.
- */
- private static final class DexDataDeallocator implements Runnable {
- private final long cookie;
-
- DexDataDeallocator(long cookie) {
- this.cookie = cookie;
- }
-
- public void run() {
- DexData.uninitialize(cookie);
- }
+ this(new ByteBuffer[] { dexBuffer }, parent);
}
}
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
index f96ed0b..85b52f8 100644
--- a/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -16,6 +16,7 @@
package dalvik.system;
+import dalvik.annotation.optimization.FastNative;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.HashMap;
@@ -106,6 +107,7 @@
*
* @return the time in milliseconds, or -1 if the debugger is not connected
*/
+ @FastNative
public static native long lastDebuggerActivity();
/**
@@ -114,6 +116,7 @@
*
* @return true if debugging is enabled
*/
+ @FastNative
public static native boolean isDebuggingEnabled();
/**
@@ -121,6 +124,7 @@
*
* @return true if (and only if) a debugger is connected
*/
+ @FastNative
public static native boolean isDebuggerConnected();
/**
@@ -251,6 +255,7 @@
* @return the CPU usage. A value of -1 means the system does not support
* this feature.
*/
+ @FastNative
public static native long threadCpuTimeNanos();
/**
@@ -291,6 +296,7 @@
/**
* Dumps a list of loaded class to the log file.
*/
+ @FastNative
public static native void printLoadedClasses(int flags);
/**
@@ -298,6 +304,7 @@
*
* @return the number of loaded classes
*/
+ @FastNative
public static native int getLoadedClassCount();
/**
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
index 7717fd9..5a2c06d 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
@@ -16,6 +16,7 @@
package org.apache.harmony.dalvik.ddmc;
+import dalvik.annotation.optimization.FastNative;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
@@ -97,6 +98,7 @@
}
/* send a chunk to the DDM server */
+ @FastNative
native private static void nativeSendChunk(int type, byte[] data,
int offset, int length);
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
index 01293b0..786efe7 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
@@ -16,6 +16,8 @@
package org.apache.harmony.dalvik.ddmc;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Declarations for some VM-internal DDM stuff.
*/
@@ -40,6 +42,7 @@
* @return true on success. false if 'when' is bad or if there was
* an internal error.
*/
+ @FastNative
native public static boolean heapInfoNotify(int when);
/**
@@ -74,11 +77,13 @@
* Return a boolean indicating whether or not the "recent allocation"
* feature is currently enabled.
*/
+ @FastNative
native public static boolean getRecentAllocationStatus();
/**
* Fill a buffer with data on recent heap allocations.
*/
+ @FastNative
native public static byte[] getRecentAllocations();
}
diff --git a/dex/src/main/java/com/android/dex/DexFormat.java b/dex/src/main/java/com/android/dex/DexFormat.java
index c598eee..97771d8 100644
--- a/dex/src/main/java/com/android/dex/DexFormat.java
+++ b/dex/src/main/java/com/android/dex/DexFormat.java
@@ -23,14 +23,38 @@
public final class DexFormat {
private DexFormat() {}
+ /** API level to target in order to generate invoke-polymorphic */
+ public static final int API_INVOKE_POLYMORPHIC = 26;
+
+ /** API level to target in order to pass through default and static interface methods */
+ public static final int API_DEFAULT_INTERFACE_METHODS = 24;
+
+ /** API level to target in order to suppress extended opcode usage */
+ public static final int API_NO_EXTENDED_OPCODES = 13;
+
/**
* API level to target in order to produce the most modern file
* format
*/
- public static final int API_CURRENT = 24;
+ public static final int API_CURRENT = API_INVOKE_POLYMORPHIC;
- /** API level to target in order to suppress extended opcode usage */
- public static final int API_NO_EXTENDED_OPCODES = 13;
+ /** dex file version number for API level 26 and earlier */
+ public static final String VERSION_FOR_API_26 = "038";
+
+ /** dex file version number for API level 24 and earlier */
+ public static final String VERSION_FOR_API_24 = "037";
+
+ /** dex file version number for API level 13 and earlier */
+ public static final String VERSION_FOR_API_13 = "035";
+
+ /**
+ * Dex file version number for dalvik.
+ * <p>
+ * Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or
+ * completed and is not considered a valid dex file format.
+ * </p>
+ */
+ public static final String VERSION_CURRENT = VERSION_FOR_API_26;
/**
* file name of the primary {@code .dex} file inside an
@@ -45,18 +69,6 @@
public static final String MAGIC_SUFFIX = "\0";
/**
- * Dex file version number for dalvik.
- * <p>
- * Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or
- * completed and is not considered a valid dex file format.
- * </p>
- */
- public static final String VERSION_CURRENT = "037";
-
- /** dex file version number for API level 13 and earlier */
- public static final String VERSION_FOR_API_13 = "035";
-
- /**
* value used to indicate endianness of file contents
*/
public static final int ENDIAN_TAG = 0x12345678;
@@ -91,10 +103,14 @@
String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]);
- if (version.equals(VERSION_CURRENT)) {
- return API_CURRENT;
- } else if (version.equals(VERSION_FOR_API_13)) {
+ if (version.equals(VERSION_FOR_API_13)) {
return API_NO_EXTENDED_OPCODES;
+ } else if (version.equals(VERSION_FOR_API_24)) {
+ return API_DEFAULT_INTERFACE_METHODS;
+ } else if (version.equals(VERSION_FOR_API_26)) {
+ return API_INVOKE_POLYMORPHIC;
+ } else if (version.equals(VERSION_CURRENT)) {
+ return API_CURRENT;
}
return -1;
@@ -108,6 +124,10 @@
if (targetApiLevel >= API_CURRENT) {
version = VERSION_CURRENT;
+ } else if (targetApiLevel >= API_INVOKE_POLYMORPHIC) {
+ version = VERSION_FOR_API_26;
+ } else if (targetApiLevel >= API_DEFAULT_INTERFACE_METHODS) {
+ version = VERSION_FOR_API_24;
} else {
version = VERSION_FOR_API_13;
}
@@ -117,6 +137,6 @@
public static boolean isSupportedDexMagic(byte[] magic) {
int api = magicToApi(magic);
- return api == API_NO_EXTENDED_OPCODES || api == API_CURRENT;
+ return api > 0;
}
}
diff --git a/dex/src/main/java/com/android/dex/TableOfContents.java b/dex/src/main/java/com/android/dex/TableOfContents.java
index 583f195..b33a749 100644
--- a/dex/src/main/java/com/android/dex/TableOfContents.java
+++ b/dex/src/main/java/com/android/dex/TableOfContents.java
@@ -36,6 +36,8 @@
public final Section fieldIds = new Section(0x0004);
public final Section methodIds = new Section(0x0005);
public final Section classDefs = new Section(0x0006);
+ public final Section callSiteIds = new Section(0x0007);
+ public final Section methodHandles = new Section(0x0008);
public final Section mapList = new Section(0x1000);
public final Section typeLists = new Section(0x1001);
public final Section annotationSetRefLists = new Section(0x1002);
@@ -48,9 +50,9 @@
public final Section encodedArrays = new Section(0x2005);
public final Section annotationsDirectories = new Section(0x2006);
public final Section[] sections = {
- header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList,
- typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas,
- debugInfos, annotations, encodedArrays, annotationsDirectories
+ header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, callSiteIds,
+ methodHandles, typeLists, annotationSetRefLists, annotationSets, classDatas, codes,
+ stringDatas, debugInfos, annotations, encodedArrays, annotationsDirectories
};
public int apiLevel;
@@ -76,7 +78,12 @@
byte[] magic = headerIn.readByteArray(8);
if (!DexFormat.isSupportedDexMagic(magic)) {
- throw new DexException("Unexpected magic: " + Arrays.toString(magic));
+ String msg =
+ String.format("Unexpected magic: [0x%02x, 0x%02x, 0x%02x, 0x%02x, "
+ + "0x%02x, 0x%02x, 0x%02x, 0x%02x]",
+ magic[0], magic[1], magic[2], magic[3],
+ magic[4], magic[5], magic[6], magic[7]);
+ throw new DexException(msg);
}
apiLevel = DexFormat.magicToApi(magic);
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
index 5dc7ad8..cd24094 100644
--- a/expectations/brokentests.txt
+++ b/expectations/brokentests.txt
@@ -45,38 +45,20 @@
description: "Some tests depend on ICU data, which has changed. Others make assumptions about floating point rounding",
result: EXEC_FAILED,
names: [
- "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_BigDecimalExceptionOrder",
"org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_DateTimeConversion",
- "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionE",
- "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionF",
- "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionG",
- "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatDoubleBigDecimalExceptionOrder",
"org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_GeneralConversionOther",
"org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_LineSeparator",
- "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Percent",
- "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Width"
+ "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Percent"
]
},
{
description: "(Needs investigation) Some tests make assertions that don't make sense, others use broken port allocation logic.",
result: EXEC_FAILED,
names: [
- "org.apache.harmony.tests.java.net.Inet6AddressTest#test_getByNameLjava_lang_String",
- "org.apache.harmony.tests.java.net.InetAddressTest#test_getByNameLjava_lang_String",
"org.apache.harmony.tests.java.net.InetAddressTest#test_isReachableLjava_net_NetworkInterfaceII_loopbackInterface"
]
},
{
- description: "(Needs investigation) Test failures from the harmony import of external/apache-harmony/archive",
- bug: 12189307,
- result: EXEC_FAILED,
- names: [
- "org.apache.harmony.tests.java.util.jar.ManifestTest#testNul",
- "org.apache.harmony.tests.java.util.jar.ManifestTest#testRead",
- "org.apache.harmony.tests.java.util.jar.ManifestTest#testStreamConstructor"
- ]
-},
-{
description: "Potentially flakey because they rely on a specific local TCP port being free.",
result: EXEC_FAILED,
names: [
@@ -123,13 +105,6 @@
]
},
{
- description: "Suffers from side effect of other, currently unknown test",
- result: EXEC_FAILED,
- names: [
- "org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnectionTest#testProxyAuthorization"
- ]
-},
-{
description: "Support_TestWebServer requires isolation.",
result: EXEC_FAILED,
names: [
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index a5764f5..da96678 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1294,19 +1294,6 @@
name: "org.apache.harmony.tests.java.lang.MathTest#test_powDD"
},
{
- description: "Known failures in PropertiesTest: We don't deal with comments in store()",
- bug: 11686302,
- result: EXEC_FAILED,
- names: [
- "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario0",
- "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario1",
- "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario2",
- "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario3",
- "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario9",
- "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario11"
- ]
-},
-{
description: "Known failures in URLTest and URLDecoderTest",
bug: 11686814,
names: [
@@ -1413,14 +1400,6 @@
]
},
{
- description: "Known precision issue in DecimalFormat",
- bug: 17656132,
- names: [
- "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatDouble_bug17656132",
- "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatDouble_roundingProblemCases"
- ]
-},
-{
description: "Known failure in GregorianCalendarTest",
bug: 12778197,
name: "org.apache.harmony.tests.java.util.GregorianCalendarTest#test_computeTime"
@@ -1435,7 +1414,7 @@
},
{
description: "libcore.java.text.DecimalFormatSymbolsTest#test_getInstance_unknown_or_invalid_locale assumes fallback to locale other than en_US_POSIX.",
- bug: 17374604,
+ bug: 17422813,
names: [
"libcore.java.text.DecimalFormatSymbolsTest#test_getInstance_unknown_or_invalid_locale"
]
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java
index 5a80fde..34dd0fa 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java
@@ -49,7 +49,24 @@
/**
* java.lang.Runtime#gc()
*/
- public void test_gcInteraction() {
+ public void test_gcInteraction_Runtime() {
+ check_gcInteraction(() -> { Runtime.getRuntime().gc(); } );
+ }
+
+ /**
+ * Checks that the sequence {@link System#gc()}, {@link System#runFinalization()}}
+ * also has the effect as asserted for {@link Runtime#gc()} elsewhere. The
+ * conditions under which System.gc() results in a garbage collection are an
+ * implementation detail not guaranteed by documentation.
+ */
+ public void test_gcInteraction_System() {
+ check_gcInteraction(() -> {
+ System.gc();
+ System.runFinalization();
+ } );
+ }
+
+ private void check_gcInteraction(Runnable gc) {
class TestPhantomReference<T> extends PhantomReference<T> {
public TestPhantomReference(T referent,
ReferenceQueue<? super T> q) {
@@ -58,7 +75,7 @@
public boolean enqueue() {
// Initiate another GC from inside enqueue() to
// see if it causes any problems inside the VM.
- Runtime.getRuntime().gc();
+ gc.run();
return super.enqueue();
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java
index 785a303..b32a965 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java
@@ -681,49 +681,6 @@
.isIPv4CompatibleAddress());
}
- public void test_getByNameLjava_lang_String() throws Exception {
- // ones to add "::255.255.255.255", "::FFFF:0.0.0.0",
- // "0.0.0.0.0.0::255.255.255.255", "F:F:F:F:F:F:F:F",
- // "[F:F:F:F:F:F:F:F]"
- String validIPAddresses[] = { "::1.2.3.4", "::", "::", "1::0", "1::",
- "::1", "0", /* jdk1.5 accepts 0 as valid */
- "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
- "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255",
- "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0.0.0.0" };
-
- String invalidIPAddresses[] = { "FFFF:FFFF" };
-
- for (int i = 0; i < validIPAddresses.length; i++) {
-
- InetAddress.getByName(validIPAddresses[i]);
-
- //exercise positive cache
- InetAddress.getByName(validIPAddresses[i]);
-
- if (!validIPAddresses[i].equals("0")) {
- String tempIPAddress = "[" + validIPAddresses[i] + "]";
- InetAddress.getByName(tempIPAddress);
- }
- }
-
- for (int i = 0; i < invalidIPAddresses.length; i++) {
- try {
- InetAddress.getByName(invalidIPAddresses[i]);
- fail("Invalid IP address incorrectly recognized as valid: "
- + invalidIPAddresses[i]);
- } catch (Exception e) {
- }
-
- //exercise negative cache
- try {
- InetAddress.getByName(invalidIPAddresses[i]);
- fail("Invalid IP address incorrectly recognized as valid: "
- + invalidIPAddresses[i]);
- } catch (Exception e) {
- }
- }
- }
-
public void test_getByAddressLString$BI() throws UnknownHostException {
try {
Inet6Address.getByAddress("123", null, 0);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
index 4e41c2a..7e232cc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
@@ -112,23 +112,6 @@
}
/**
- * java.net.InetAddress#getByName(java.lang.String)
- */
- public void test_getByNameLjava_lang_String() throws Exception {
- // Test for method java.net.InetAddress
- // java.net.InetAddress.getByName(java.lang.String)
- InetAddress ia2 = InetAddress.getByName("127.0.0.1");
-
- // TODO : Test to ensure all the address formats are recognized
- InetAddress i = InetAddress.getByName("1.2.3");
- assertEquals("1.2.0.3", i.getHostAddress());
- i = InetAddress.getByName("1.2");
- assertEquals("1.0.0.2", i.getHostAddress());
- i = InetAddress.getByName(String.valueOf(0xffffffffL));
- assertEquals("255.255.255.255", i.getHostAddress());
- }
-
- /**
* java.net.InetAddress#getHostAddress()
*/
public void test_getHostAddress() throws Exception {
@@ -383,12 +366,11 @@
NetworkInterface loopbackInterface = null;
ArrayList<InetAddress> localAddresses = new ArrayList<InetAddress>();
- Enumeration<NetworkInterface> networkInterfaces = NetworkInterface
- .getNetworkInterfaces();
+ Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+ assertNotNull(networkInterfaces);
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
- Enumeration<InetAddress> addresses = networkInterface
- .getInetAddresses();
+ Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
if (address.isLoopbackAddress()) {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
index 902caef..29470d1 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
@@ -1699,7 +1699,7 @@
// double 9999999999.999998 is decimal 9999999999.9999980926513671875
assertEquals("9999999999.999998", df.format(9999999999.999998));
// double 1E23 is decimal 99999999999999991611392
- assertEquals("9999999999999999", df.format(1E23));
+ assertEquals("99999999999999990000000", df.format(1E23));
}
public void test_getDecimalFormatSymbols() {
diff --git a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
index fc1632c..ca2add3 100644
--- a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
+++ b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
@@ -1221,21 +1221,61 @@
fail("Unexpected thread termination");
else if (millisElapsedSince(startTime) > timeoutMillis) {
threadAssertTrue(thread.isAlive());
- return;
+ fail("timed out waiting for thread to enter wait state");
}
Thread.yield();
}
}
/**
- * Waits up to LONG_DELAY_MS for the given thread to enter a wait
- * state: BLOCKED, WAITING, or TIMED_WAITING.
+ * Spin-waits up to the specified number of milliseconds for the given
+ * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING,
+ * and additionally satisfy the given condition.
+ */
+ void waitForThreadToEnterWaitState(
+ Thread thread, long timeoutMillis, Callable<Boolean> waitingForGodot) {
+ long startTime = 0L;
+ for (;;) {
+ Thread.State s = thread.getState();
+ if (s == Thread.State.BLOCKED ||
+ s == Thread.State.WAITING ||
+ s == Thread.State.TIMED_WAITING) {
+ try {
+ if (waitingForGodot.call())
+ return;
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+ else if (s == Thread.State.TERMINATED)
+ fail("Unexpected thread termination");
+ else if (startTime == 0L)
+ startTime = System.nanoTime();
+ else if (millisElapsedSince(startTime) > timeoutMillis) {
+ threadAssertTrue(thread.isAlive());
+ fail("timed out waiting for thread to enter wait state");
+ }
+ Thread.yield();
+ }
+ }
+
+ /**
+ * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to
+ * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING.
*/
void waitForThreadToEnterWaitState(Thread thread) {
waitForThreadToEnterWaitState(thread, LONG_DELAY_MS);
}
/**
+ * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to
+ * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING,
+ * and additionally satisfy the given condition.
+ */
+ void waitForThreadToEnterWaitState(
+ Thread thread, Callable<Boolean> waitingForGodot) {
+ waitForThreadToEnterWaitState(thread, LONG_DELAY_MS, waitingForGodot);
+ }
+
+ /**
* Returns the number of milliseconds since time given by
* startNanoTime, which must have been previously returned from a
* call to {@link System#nanoTime()}.
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
index 05fc689..efe5a58 100644
--- a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
@@ -17,6 +17,7 @@
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
@@ -750,9 +751,11 @@
}});
threadStarted.await();
- waitForThreadToEnterWaitState(t);
- assertEquals(1, q.getWaitingConsumerCount());
- assertTrue(q.hasWaitingConsumer());
+ Callable<Boolean> oneConsumer
+ = new Callable<Boolean>() { public Boolean call() {
+ return q.hasWaitingConsumer()
+ && q.getWaitingConsumerCount() == 1; }};
+ waitForThreadToEnterWaitState(t, oneConsumer);
assertTrue(q.offer(one));
assertEquals(0, q.getWaitingConsumerCount());
@@ -789,8 +792,11 @@
}});
threadStarted.await();
- waitForThreadToEnterWaitState(t);
- assertEquals(1, q.size());
+ Callable<Boolean> oneElement
+ = new Callable<Boolean>() { public Boolean call() {
+ return !q.isEmpty() && q.size() == 1; }};
+ waitForThreadToEnterWaitState(t, oneElement);
+
assertSame(five, q.poll());
checkEmpty(q);
awaitTermination(t);
diff --git a/jsr166-tests/src/test/java/jsr166/PhaserTest.java b/jsr166-tests/src/test/java/jsr166/PhaserTest.java
index 673e556..1219017 100644
--- a/jsr166-tests/src/test/java/jsr166/PhaserTest.java
+++ b/jsr166-tests/src/test/java/jsr166/PhaserTest.java
@@ -527,7 +527,7 @@
}});
await(pleaseArrive);
- waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+ waitForThreadToEnterWaitState(t);
assertEquals(0, phaser.arrive());
awaitTermination(t);
@@ -555,7 +555,7 @@
}});
await(pleaseArrive);
- waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+ waitForThreadToEnterWaitState(t);
t.interrupt();
assertEquals(0, phaser.arrive());
awaitTermination(t);
@@ -571,20 +571,20 @@
public void testArriveAndAwaitAdvanceAfterInterrupt() {
final Phaser phaser = new Phaser();
assertEquals(0, phaser.register());
- final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+ final CountDownLatch pleaseArrive = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() {
Thread.currentThread().interrupt();
assertEquals(0, phaser.register());
- pleaseInterrupt.countDown();
+ pleaseArrive.countDown();
assertTrue(Thread.currentThread().isInterrupted());
assertEquals(1, phaser.arriveAndAwaitAdvance());
- assertTrue(Thread.currentThread().isInterrupted());
+ assertTrue(Thread.interrupted());
}});
- await(pleaseInterrupt);
- waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+ await(pleaseArrive);
+ waitForThreadToEnterWaitState(t);
Thread.currentThread().interrupt();
assertEquals(1, phaser.arriveAndAwaitAdvance());
assertTrue(Thread.interrupted());
@@ -605,11 +605,11 @@
assertFalse(Thread.currentThread().isInterrupted());
pleaseInterrupt.countDown();
assertEquals(1, phaser.arriveAndAwaitAdvance());
- assertTrue(Thread.currentThread().isInterrupted());
+ assertTrue(Thread.interrupted());
}});
await(pleaseInterrupt);
- waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+ waitForThreadToEnterWaitState(t);
t.interrupt();
Thread.currentThread().interrupt();
assertEquals(1, phaser.arriveAndAwaitAdvance());
@@ -784,7 +784,7 @@
assertEquals(THREADS, phaser.getArrivedParties());
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
for (Thread thread : threads)
- waitForThreadToEnterWaitState(thread, SHORT_DELAY_MS);
+ waitForThreadToEnterWaitState(thread);
for (Thread thread : threads)
assertTrue(thread.isAlive());
assertState(phaser, 0, THREADS + 1, 1);
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index e53e103..44930a6 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -16,6 +16,7 @@
package dalvik.system;
+import dalvik.annotation.optimization.FastNative;
import java.lang.ref.FinalizerReference;
import java.util.HashMap;
import java.util.Map;
@@ -102,11 +103,13 @@
/**
* Returns whether the VM is running in 64-bit mode.
*/
+ @FastNative
public native boolean is64Bit();
/**
* Returns whether the VM is running with JNI checking enabled.
*/
+ @FastNative
public native boolean isCheckJniEnabled();
/**
@@ -255,6 +258,7 @@
* This is used to implement native allocations on the Java heap, such as DirectByteBuffers
* and Bitmaps.
*/
+ @FastNative
public native Object newNonMovableArray(Class<?> componentType, int length);
/**
@@ -262,12 +266,14 @@
* avoiding any padding after the array. The amount of padding varies depending on the
* componentType and the memory allocator implementation.
*/
+ @FastNative
public native Object newUnpaddedArray(Class<?> componentType, int minLength);
/**
* Returns the address of array[0]. This differs from using JNI in that JNI might lie and
* give you the address of a copy of the array when in forcecopy mode.
*/
+ @FastNative
public native long addressOf(Object array);
/**
@@ -285,11 +291,13 @@
/**
* Returns true if either a Java debugger or native debugger is active.
*/
+ @FastNative
public native boolean isDebuggerActive();
/**
* Returns true if native debugging is on.
*/
+ @FastNative
public native boolean isNativeDebuggable();
/**
diff --git a/libart/src/main/java/dalvik/system/VMStack.java b/libart/src/main/java/dalvik/system/VMStack.java
index b69ab60..ef911c4 100644
--- a/libart/src/main/java/dalvik/system/VMStack.java
+++ b/libart/src/main/java/dalvik/system/VMStack.java
@@ -16,6 +16,8 @@
package dalvik.system;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Provides a limited interface to the Dalvik VM stack. This class is mostly
* used for implementing security checks.
@@ -29,6 +31,7 @@
* @return the requested class loader, or {@code null} if this is the
* bootstrap class loader.
*/
+ @FastNative
native public static ClassLoader getCallingClassLoader();
/**
@@ -45,12 +48,14 @@
*
* @return the requested class, or {@code null}.
*/
+ @FastNative
native public static Class<?> getStackClass2();
/**
* Returns the first ClassLoader on the call stack that isn't the
* bootstrap class loader.
*/
+ @FastNative
public native static ClassLoader getClosestUserClassLoader();
/**
@@ -61,6 +66,7 @@
* @return an array of stack trace elements, or null if the thread
* doesn't have a stack trace (e.g. because it exited)
*/
+ @FastNative
native public static StackTraceElement[] getThreadStackTrace(Thread t);
/**
@@ -74,6 +80,7 @@
* desired. Unused elements will be filled with null values.
* @return the number of elements filled
*/
+ @FastNative
native public static int fillStackTraceElements(Thread t,
StackTraceElement[] stackTraceElements);
}
diff --git a/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java b/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
index 13e9317..5a84c8e 100644
--- a/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
+++ b/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
@@ -106,6 +106,9 @@
// Hardcode default value for AVA. b/28174137
{ "com.sun.security.preserveOldDCEncoding", null },
+
+ // Hardcode default value for LogManager. b/28174137
+ { "java.util.logging.manager", null },
};
}
diff --git a/libart/src/main/java/java/lang/CaseMapper.java b/libart/src/main/java/java/lang/CaseMapper.java
index 7f9d2e3..66d5030 100644
--- a/libart/src/main/java/java/lang/CaseMapper.java
+++ b/libart/src/main/java/java/lang/CaseMapper.java
@@ -50,7 +50,7 @@
return ICU.toLowerCase(s, locale);
}
- String newString = null;
+ char[] newValue = null;
for (int i = 0, end = s.length(); i < end; ++i) {
char ch = s.charAt(i);
char newCh;
@@ -63,13 +63,14 @@
newCh = Character.toLowerCase(ch);
}
if (ch != newCh) {
- if (newString == null) {
- newString = StringFactory.newStringFromString(s);
+ if (newValue == null) {
+ newValue = new char[end];
+ s.getCharsNoCheck(0, end, newValue, 0);
}
- newString.setCharAt(i, newCh);
+ newValue[i] = newCh;
}
}
- return newString != null ? newString : s;
+ return newValue != null ? new String(newValue) : s;
}
/**
@@ -152,9 +153,8 @@
}
char[] output = null;
- String newString = null;
int i = 0;
- for (int o = 0, end = count; o < end; o++) {
+ for (int o = 0; o < count; o++) {
char ch = s.charAt(o);
if (Character.isHighSurrogate(ch)) {
return ICU.toUpperCase(s, locale);
@@ -170,10 +170,10 @@
if (output != null) {
output[i++] = upch;
} else if (ch != upch) {
- if (newString == null) {
- newString = StringFactory.newStringFromString(s);
- }
- newString.setCharAt(o, upch);
+ output = new char[count];
+ i = o;
+ s.getCharsNoCheck(0, i, output, 0);
+ output[i++] = upch;
}
} else {
int target = index * 3;
@@ -181,11 +181,7 @@
if (output == null) {
output = new char[count + (count / 6) + 2];
i = o;
- if (newString != null) {
- System.arraycopy(newString.toCharArray(), 0, output, 0, i);
- } else {
- System.arraycopy(s.toCharArray(), 0, output, 0, i);
- }
+ s.getCharsNoCheck(0, i, output, 0);
} else if (i + (val3 == 0 ? 1 : 2) >= output.length) {
char[] newoutput = new char[output.length + (count / 6) + 3];
System.arraycopy(output, 0, newoutput, 0, output.length);
@@ -202,11 +198,7 @@
}
}
if (output == null) {
- if (newString != null) {
- return newString;
- } else {
- return s;
- }
+ return s;
}
return output.length == i || output.length - i < 8 ? new String(0, i, output) : new String(output, 0, i);
}
diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java
index 8465a24..7e5bdff 100644
--- a/libart/src/main/java/java/lang/DexCache.java
+++ b/libart/src/main/java/java/lang/DexCache.java
@@ -32,6 +32,7 @@
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import com.android.dex.Dex;
/**
@@ -48,12 +49,24 @@
private long dexFile;
/**
+ * References to CallSite (C array pointer) as they become resolved following
+ * interpreter semantics.
+ */
+ private long resolvedCallSites;
+
+ /**
* References to fields (C array pointer) as they become resolved following
* interpreter semantics. May refer to fields defined in other dex files.
*/
private long resolvedFields;
/**
+ * References to MethodType (C array pointer) as they become resolved following
+ * interpreter semantics.
+ */
+ private long resolvedMethodTypes;
+
+ /**
* References to methods (C array pointer) as they become resolved following
* interpreter semantics. May refer to methods defined in other dex files.
*/
@@ -72,10 +85,9 @@
private long strings;
/**
- * References to MethodType (C array pointer) as they become resolved following
- * interpreter semantics.
+ * The number of elements in the native call sites array.
*/
- private long resolvedMethodTypes;
+ private int numResolvedCallSites;
/**
* The number of elements in the native resolvedFields array.
@@ -83,6 +95,11 @@
private int numResolvedFields;
/**
+ * The number of elements in the native method types array.
+ */
+ private int numResolvedMethodTypes;
+
+ /**
* The number of elements in the native resolvedMethods array.
*/
private int numResolvedMethods;
@@ -97,11 +114,6 @@
*/
private int numStrings;
- /**
- * The number of elements in the native method types array.
- */
- private int numResolvedMethodTypes;
-
// Only created by the VM.
private DexCache() {}
@@ -118,10 +130,15 @@
return result;
}
+ @FastNative
native Class<?> getResolvedType(int typeIndex);
+ @FastNative
native String getResolvedString(int stringIndex);
+ @FastNative
native void setResolvedType(int typeIndex, Class<?> type);
+ @FastNative
native void setResolvedString(int stringIndex, String string);
+ @FastNative
private native Dex getDexNative();
}
diff --git a/libart/src/main/java/java/lang/StringFactory.java b/libart/src/main/java/java/lang/StringFactory.java
index 0a89740..208a657 100644
--- a/libart/src/main/java/java/lang/StringFactory.java
+++ b/libart/src/main/java/java/lang/StringFactory.java
@@ -17,6 +17,7 @@
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
@@ -53,6 +54,7 @@
return newStringFromBytes(data, offset, byteCount, Charset.defaultCharset());
}
+ @FastNative
public static native String newStringFromBytes(byte[] data, int high, int offset, int byteCount);
public static String newStringFromBytes(byte[] data, int offset, int byteCount, String charsetName) throws UnsupportedEncodingException {
@@ -219,8 +221,10 @@
}
// The char array passed as {@code java_data} must not be a null reference.
+ @FastNative
static native String newStringFromChars(int offset, int charCount, char[] data);
+ @FastNative
public static native String newStringFromString(String toCopy);
public static String newStringFromStringBuffer(StringBuffer stringBuffer) {
diff --git a/libart/src/main/java/java/lang/VMClassLoader.java b/libart/src/main/java/java/lang/VMClassLoader.java
index f5e65c8..d44f888 100644
--- a/libart/src/main/java/java/lang/VMClassLoader.java
+++ b/libart/src/main/java/java/lang/VMClassLoader.java
@@ -16,6 +16,7 @@
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import java.io.File;
import java.io.IOException;
import java.net.URL;
@@ -85,6 +86,7 @@
return list;
}
+ @FastNative
native static Class findLoadedClass(ClassLoader cl, String name);
/**
diff --git a/luni/src/main/java/java/lang/ref/FinalizerReference.java b/luni/src/main/java/java/lang/ref/FinalizerReference.java
index 61bfb6c..d7e803e 100644
--- a/luni/src/main/java/java/lang/ref/FinalizerReference.java
+++ b/luni/src/main/java/java/lang/ref/FinalizerReference.java
@@ -16,6 +16,8 @@
package java.lang.ref;
+import dalvik.annotation.optimization.FastNative;
+
/**
* @hide
*/
@@ -129,7 +131,9 @@
throw new AssertionError("newly-created live Sentinel not on list!");
}
+ @FastNative
private final native T getReferent();
+ @FastNative
private native boolean makeCircularListIfUnenqueued();
/**
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 18aae9b..111c584 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -201,7 +201,7 @@
@Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
- if ((mode & O_ACCMODE) != O_RDONLY) {
+ if ((flags & O_ACCMODE) != O_RDONLY) {
BlockGuard.getThreadPolicy().onWriteToDisk();
}
return os.open(path, flags, mode);
diff --git a/luni/src/main/java/libcore/io/Memory.java b/luni/src/main/java/libcore/io/Memory.java
index e148457..ba7398d 100644
--- a/luni/src/main/java/libcore/io/Memory.java
+++ b/luni/src/main/java/libcore/io/Memory.java
@@ -17,6 +17,7 @@
package libcore.io;
+import dalvik.annotation.optimization.FastNative;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -150,6 +151,7 @@
*/
public static native void memmove(Object dstObject, int dstOffset, Object srcObject, int srcOffset, long byteCount);
+ @FastNative
public static native byte peekByte(long address);
public static int peekInt(long address, boolean swap) {
@@ -159,6 +161,7 @@
}
return result;
}
+ @FastNative
private static native int peekIntNative(long address);
public static long peekLong(long address, boolean swap) {
@@ -168,6 +171,7 @@
}
return result;
}
+ @FastNative
private static native long peekLongNative(long address);
public static short peekShort(long address, boolean swap) {
@@ -177,6 +181,7 @@
}
return result;
}
+ @FastNative
private static native short peekShortNative(long address);
public static native void peekByteArray(long address, byte[] dst, int dstOffset, int byteCount);
@@ -187,6 +192,7 @@
public static native void peekLongArray(long address, long[] dst, int dstOffset, int longCount, boolean swap);
public static native void peekShortArray(long address, short[] dst, int dstOffset, int shortCount, boolean swap);
+ @FastNative
public static native void pokeByte(long address, byte value);
public static void pokeInt(long address, int value, boolean swap) {
@@ -195,6 +201,7 @@
}
pokeIntNative(address, value);
}
+ @FastNative
private static native void pokeIntNative(long address, int value);
public static void pokeLong(long address, long value, boolean swap) {
@@ -203,6 +210,7 @@
}
pokeLongNative(address, value);
}
+ @FastNative
private static native void pokeLongNative(long address, long value);
public static void pokeShort(long address, short value, boolean swap) {
@@ -211,6 +219,7 @@
}
pokeShortNative(address, value);
}
+ @FastNative
private static native void pokeShortNative(long address, short value);
public static native void pokeByteArray(long address, byte[] src, int offset, int count);
diff --git a/luni/src/main/java/libcore/util/CharsetUtils.java b/luni/src/main/java/libcore/util/CharsetUtils.java
index 5163dba..bab6f53 100644
--- a/luni/src/main/java/libcore/util/CharsetUtils.java
+++ b/luni/src/main/java/libcore/util/CharsetUtils.java
@@ -16,6 +16,8 @@
package libcore.util;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Various special-case charset conversions (for performance).
*
@@ -26,18 +28,21 @@
* Returns a new byte array containing the bytes corresponding to the characters in the given
* string, encoded in US-ASCII. Unrepresentable characters are replaced by (byte) '?'.
*/
+ @FastNative
public static native byte[] toAsciiBytes(String s, int offset, int length);
/**
* Returns a new byte array containing the bytes corresponding to the characters in the given
* string, encoded in ISO-8859-1. Unrepresentable characters are replaced by (byte) '?'.
*/
+ @FastNative
public static native byte[] toIsoLatin1Bytes(String s, int offset, int length);
/**
* Returns a new byte array containing the bytes corresponding to the characters in the given
* string, encoded in UTF-8. All characters are representable in UTF-8.
*/
+ @FastNative
public static native byte[] toUtf8Bytes(String s, int offset, int length);
/**
@@ -64,6 +69,7 @@
* value[i] = (ch <= 0x7f) ? ch : REPLACEMENT_CHAR;
* }
*/
+ @FastNative
public static native void asciiBytesToChars(byte[] bytes, int offset, int length, char[] chars);
/**
@@ -73,6 +79,7 @@
* value[i] = (char) (data[start++] & 0xff);
* }
*/
+ @FastNative
public static native void isoLatin1BytesToChars(byte[] bytes, int offset, int length, char[] chars);
private CharsetUtils() {
diff --git a/luni/src/main/native/java_math_NativeBN.cpp b/luni/src/main/native/java_math_NativeBN.cpp
index c29a6c7..5c42be6 100644
--- a/luni/src/main/native/java_math_NativeBN.cpp
+++ b/luni/src/main/native/java_math_NativeBN.cpp
@@ -216,43 +216,33 @@
if (bytes.get() == NULL) {
return;
}
+
+ if (bytesLen == 0) {
+ BN_zero(ret);
+ return;
+ }
+
const unsigned char* bytesTmp = reinterpret_cast<const unsigned char*>(bytes.get());
- if ((bytes[0] & 0x80) == 0) {
- // Positive value: we can use the existing BN implementation for unsigned big endian bytes.
+ if (!BN_bin2bn(bytesTmp, bytesLen, ret)) {
+ throwException(env);
+ return;
+ }
- if (!BN_bin2bn(bytesTmp, bytesLen, ret)) {
- throwException(env);
- return;
- }
- BN_set_negative(ret, false);
- } else {
- // Negative value: we need to interpret the twos complement representation
- //
- // This uses the fact that in the 2c rep,
- // - x = ~ x + 1
- //
- // TODO (varomodt): add BN bitwise ops to make this easier.
+ // Use the high bit to determine the sign in twos-complement.
+ BN_set_negative(ret, (bytes[0] & 0x80) != 0);
- jbyteArray oppositeBytes = env->NewByteArray(bytesLen);
- ScopedByteArrayRW opp(env, oppositeBytes);
- uint8_t* oppTmp = reinterpret_cast<uint8_t*>(opp.get());
-
- for (int i = 0; i < bytesLen; i++) {
- oppTmp[i] = ~ bytesTmp[i];
- }
-
- if (!BN_bin2bn(oppTmp, bytesLen, ret)) {
+ if (BN_is_negative(ret)) {
+ // For negative values, BN_bin2bn doesn't interpret the twos-complement
+ // representation, so ret is now (- value - 2^N). We can use nnmod_pow2 to set
+ // ret to (-value).
+ if (!BN_nnmod_pow2(ret, ret, bytesLen * 8)) {
throwException(env);
return;
}
- BN_set_negative(ret, true);
-
- if (!BN_sub(ret, ret, BN_value_one())) {
- throwException(env);
- return;
- }
+ // And now we correct the sign.
+ BN_set_negative(ret, 1);
}
}
@@ -372,39 +362,31 @@
BN_set_negative(toBigNum(b), n);
}
-// TODO (varomodt): add BN_is_pow2 to make this fast.
-static bool isPowerOfTwo(JNIEnv* env, BIGNUM* x) {
- int bits = BN_num_bits(x);
-
- if (bits == 0) {
- return false;
- }
-
- BIGNUM tmp;
- BN_init(&tmp);
-
- if (!BN_copy(&tmp, x)) {
- BN_free(&tmp);
- throwException(env);
- return false;
- }
-
- // If our value is 2^bits, then masking off one bit will make it zero.
- BN_mask_bits(&tmp, bits - 1);
- bool ret = BN_is_zero(&tmp);
- BN_free(&tmp);
- return ret;
-}
-
static int NativeBN_bitLength(JNIEnv* env, jclass, jlong a0) {
if (!oneValidHandle(env, a0)) return JNI_FALSE;
BIGNUM* a = toBigNum(a0);
- if (BN_is_negative(a)) {
- return isPowerOfTwo(env, a) ? BN_num_bits(a) - 1 : BN_num_bits(a);
- } else {
+ // If a is not negative, we can use BN_num_bits directly.
+ if (!BN_is_negative(a)) {
return BN_num_bits(a);
}
+
+ // In the negative case, the number of bits in a is the same as the number of bits in |a|,
+ // except one less when |a| is a power of two.
+ BIGNUM positiveA;
+ BN_init(&positiveA);
+
+ if (!BN_copy(&positiveA, a)) {
+ BN_free(&positiveA);
+ throwException(env);
+ return -1;
+ }
+
+ BN_set_negative(&positiveA, false);
+ int numBits = BN_is_pow2(&positiveA) ? BN_num_bits(&positiveA) - 1 : BN_num_bits(&positiveA);
+
+ BN_free(&positiveA);
+ return numBits;
}
static jboolean NativeBN_BN_is_bit_set(JNIEnv* env, jclass, jlong a, int n) {
diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp
index 05680db..1acb8f4 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -18,6 +18,7 @@
#include "JNIHelp.h"
#include "JniConstants.h"
+#include "nativehelper/jni_macros.h"
#include "Portability.h"
#include "ScopedBytes.h"
#include "ScopedPrimitiveArray.h"
@@ -289,27 +290,27 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Memory, memmove, "(Ljava/lang/Object;ILjava/lang/Object;IJ)V"),
- NATIVE_METHOD(Memory, peekByte, "!(J)B"),
+ FAST_NATIVE_METHOD(Memory, peekByte, "(J)B"),
NATIVE_METHOD(Memory, peekByteArray, "(J[BII)V"),
NATIVE_METHOD(Memory, peekCharArray, "(J[CIIZ)V"),
NATIVE_METHOD(Memory, peekDoubleArray, "(J[DIIZ)V"),
NATIVE_METHOD(Memory, peekFloatArray, "(J[FIIZ)V"),
- NATIVE_METHOD(Memory, peekIntNative, "!(J)I"),
+ FAST_NATIVE_METHOD(Memory, peekIntNative, "(J)I"),
NATIVE_METHOD(Memory, peekIntArray, "(J[IIIZ)V"),
- NATIVE_METHOD(Memory, peekLongNative, "!(J)J"),
+ FAST_NATIVE_METHOD(Memory, peekLongNative, "(J)J"),
NATIVE_METHOD(Memory, peekLongArray, "(J[JIIZ)V"),
- NATIVE_METHOD(Memory, peekShortNative, "!(J)S"),
+ FAST_NATIVE_METHOD(Memory, peekShortNative, "(J)S"),
NATIVE_METHOD(Memory, peekShortArray, "(J[SIIZ)V"),
- NATIVE_METHOD(Memory, pokeByte, "!(JB)V"),
+ FAST_NATIVE_METHOD(Memory, pokeByte, "(JB)V"),
NATIVE_METHOD(Memory, pokeByteArray, "(J[BII)V"),
NATIVE_METHOD(Memory, pokeCharArray, "(J[CIIZ)V"),
NATIVE_METHOD(Memory, pokeDoubleArray, "(J[DIIZ)V"),
NATIVE_METHOD(Memory, pokeFloatArray, "(J[FIIZ)V"),
- NATIVE_METHOD(Memory, pokeIntNative, "!(JI)V"),
+ FAST_NATIVE_METHOD(Memory, pokeIntNative, "(JI)V"),
NATIVE_METHOD(Memory, pokeIntArray, "(J[IIIZ)V"),
- NATIVE_METHOD(Memory, pokeLongNative, "!(JJ)V"),
+ FAST_NATIVE_METHOD(Memory, pokeLongNative, "(JJ)V"),
NATIVE_METHOD(Memory, pokeLongArray, "(J[JIIZ)V"),
- NATIVE_METHOD(Memory, pokeShortNative, "!(JS)V"),
+ FAST_NATIVE_METHOD(Memory, pokeShortNative, "(JS)V"),
NATIVE_METHOD(Memory, pokeShortArray, "(J[SIIZ)V"),
NATIVE_METHOD(Memory, unsafeBulkGet, "(Ljava/lang/Object;II[BIIZ)V"),
NATIVE_METHOD(Memory, unsafeBulkPut, "([BIILjava/lang/Object;IIZ)V"),
diff --git a/luni/src/test/java/libcore/java/lang/OldSystemTest.java b/luni/src/test/java/libcore/java/lang/OldSystemTest.java
index dc5741f..224e697 100644
--- a/luni/src/test/java/libcore/java/lang/OldSystemTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldSystemTest.java
@@ -255,22 +255,9 @@
}
}
- public void test_gc() {
- Runtime rt = Runtime.getRuntime();
- Vector<StringBuffer> vec = new Vector<StringBuffer>();
- long beforeTest = rt.totalMemory() - rt.freeMemory();
- while (rt.totalMemory() - rt.freeMemory() < beforeTest * 2) {
- vec.add(new StringBuffer(1000));
- }
- long beforeGC = rt.totalMemory() - rt.freeMemory();
- vec = null;
- System.gc();
- System.runFinalization();
- long afterGC = rt.totalMemory() - rt.freeMemory();
- assertTrue("memory was not released after calling System.gc()." +
- "before gc: " + beforeGC + "; after gc: " + afterGC,
- beforeGC > afterGC);
- }
+ // Android-changed: test_gc() was deleted. PhantomReferenceTest provides basic
+ // coverage for the fact that System.gc() executes a garbage collection if
+ // followed by System.runFinalization().
public void test_getenv() {
// String[] props = { "PATH", "HOME", "USER"};
diff --git a/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java b/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java
new file mode 100644
index 0000000..5ba87f9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.lang.invoke;
+
+import junit.framework.TestCase;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MutableCallSite;
+import java.lang.invoke.VolatileCallSite;
+import java.lang.invoke.WrongMethodTypeException;
+
+import static java.lang.invoke.MethodHandles.Lookup.*;
+
+public class CallSitesTest extends TestCase {
+ public void test_ConstantCallSite() throws Throwable {
+ final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+ final MethodHandle mh =
+ MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+ final ConstantCallSite site = new ConstantCallSite(mh);
+ assertEquals(mh, site.getTarget());
+ assertEquals(type, site.type());
+
+ int n = (int) site.dynamicInvoker().invokeExact(7, 37);
+ assertEquals(44, n);
+ try {
+ site.setTarget(mh);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+
+ public void test_EarlyBoundMutableCallSite() throws Throwable {
+ final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+ final MethodHandle add2 =
+ MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+ MutableCallSite site = new MutableCallSite(type);
+ commonMutableCallSitesTest(site, add2);
+ }
+
+ public void test_EarlyBoundVolatileCallSite() throws Throwable {
+ final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+ final MethodHandle add2 =
+ MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+ VolatileCallSite site = new VolatileCallSite(type);
+ commonMutableCallSitesTest(site, add2);
+ }
+
+ public void test_LateBoundMutableCallSite() throws Throwable {
+ final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+ MutableCallSite site = new MutableCallSite(type);
+ assertEquals(type, site.type());
+ try {
+ int dummy = (int) site.getTarget().invokeExact(1, 1);
+ fail();
+ } catch (IllegalStateException e) {
+ assertEquals("uninitialized call site", e.getMessage());
+ }
+ final MethodHandle add2 =
+ MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+ site.setTarget(add2);
+ commonMutableCallSitesTest(site, add2);
+ }
+
+ public void test_LateBoundVolatileCallSite() throws Throwable {
+ final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+ VolatileCallSite site = new VolatileCallSite(type);
+ assertEquals(type, site.type());
+ try {
+ int dummy = (int) site.getTarget().invokeExact(1, 1);
+ fail();
+ } catch (IllegalStateException e) {
+ assertEquals("uninitialized call site", e.getMessage());
+ }
+ final MethodHandle add2 =
+ MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+ site.setTarget(add2);
+ commonMutableCallSitesTest(site, add2);
+ }
+
+ private static void commonMutableCallSitesTest(CallSite site,
+ MethodHandle firstTarget) throws Throwable{
+ site.setTarget(firstTarget);
+ site.setTarget(firstTarget);
+
+ int x = (int) firstTarget.invokeExact(2, 6);
+ assertEquals(8, x);
+
+ int y = (int) site.getTarget().invokeExact(2, 6);
+ assertEquals(8, y);
+
+ int z = (int) site.dynamicInvoker().invokeExact(2, 6);
+ assertEquals(8, z);
+
+ try {
+ site.setTarget(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ final MethodHandle other = MethodHandles.lookup().findStatic(
+ CallSitesTest.class, "add3",
+ MethodType.methodType(int.class, int.class, int.class, int.class));
+ try {
+ site.setTarget(other);
+ fail();
+ } catch (WrongMethodTypeException e) {
+ }
+ assertEquals(firstTarget, site.getTarget());
+
+ final MethodHandle sub2 =
+ MethodHandles.lookup().findStatic(CallSitesTest.class, "sub2", firstTarget.type());
+ site.setTarget(sub2);
+ assertEquals(sub2, site.getTarget());
+ assertEquals(100, (int) site.dynamicInvoker().invokeExact(147, 47));
+ }
+
+ private static int add2(int x, int y) {
+ return x + y;
+ }
+
+ private static int add3(int x, int y, int z) {
+ return x + y + z;
+ }
+
+ private static int sub2(int x, int y) {
+ return x - y;
+ }
+}
+
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
index 4c65161..9bf02ef 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
@@ -16,16 +16,20 @@
package libcore.java.lang.invoke;
+import java.lang.Thread;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.WrongMethodTypeException;
import java.util.ArrayList;
+import java.util.Arrays;
import junit.framework.TestCase;
public class MethodHandleCombinersTest extends TestCase {
+ static final int TEST_THREAD_ITERATIONS = 1000;
+
public static void testThrowException() throws Throwable {
MethodHandle handle = MethodHandles.throwException(String.class,
IllegalArgumentException.class);
@@ -117,6 +121,23 @@
}
}
+ public static void testDropArguments_List() throws Throwable {
+ MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "dropArguments_delegate",
+ MethodType.methodType(void.class, new Class<?>[]{String.class, long.class}));
+
+ MethodHandle transform = MethodHandles.dropArguments(
+ delegate, 0, Arrays.asList(int.class, Object.class));
+
+ transform.invokeExact(45, new Object(), "foo", 42l);
+ transform.invoke(45, new Object(), "foo", 42l);
+
+ // Check that asType works as expected.
+ transform = transform.asType(MethodType.methodType(void.class,
+ new Class<?>[]{short.class, Object.class, String.class, long.class}));
+ transform.invokeExact((short) 45, new Object(), "foo", 42l);
+ }
+
public static String testCatchException_target(String arg1, long arg2, String exceptionMessage)
throws Throwable {
if (exceptionMessage != null) {
@@ -1193,7 +1214,13 @@
Object ret = handle.invokeWithArguments(new Object[]{"a", "b", "c"});
assertEquals(42, (int) ret);
- handle.invokeWithArguments(new String[]{"a", "b", "c"});
+ ret = handle.invokeWithArguments(new String[]{"a", "b", "c"});
+ assertEquals(42, (int) ret);
+
+ // Also test the versions that take a List<?> instead of an array.
+ ret = handle.invokeWithArguments(Arrays.asList(new Object[] {"a", "b", "c"}));
+ assertEquals(42, (int) ret);
+ ret = handle.invokeWithArguments(Arrays.asList(new String[]{"a", "b", "c"}));
assertEquals(42, (int) ret);
// Pass in an array that's too small. Should throw an IAE.
@@ -1204,6 +1231,14 @@
} catch (WrongMethodTypeException expected) {
}
+ try {
+ handle.invokeWithArguments(Arrays.asList(new Object[]{"a", "b"}));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ } catch (WrongMethodTypeException expected) {
+ }
+
+
// Test implicit unboxing.
MethodType methodType2 = MethodType.methodType(int.class,
new Class<?>[]{String.class, int.class});
@@ -1633,4 +1668,260 @@
} catch (IllegalArgumentException expected) {
}
}
+
+ // An exception thrown on worker threads and re-thrown on the main thread.
+ static Throwable workerException = null;
+
+ private static void invokeMultiThreaded(final MethodHandle mh) throws Throwable {
+ // Create enough worker threads to be oversubscribed in bid to force some parallelism.
+ final int threadCount = Runtime.getRuntime().availableProcessors() + 1;
+ final Thread threads [] = new Thread [threadCount];
+
+ // Launch worker threads and iterate invoking method handle.
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i] = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ for (int j = 0; j < TEST_THREAD_ITERATIONS; ++j) {
+ mh.invoke();
+ }
+ } catch (Throwable t) {
+ workerException = t;
+ fail("Unexpected exception " + workerException);
+ }
+ }});
+ threads[i].start();
+ }
+
+ // Wait for completion
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i].join();
+ }
+
+ // Fail on main thread to avoid test appearing to complete successfully.
+ Throwable t = workerException;
+ workerException = null;
+ if (t != null) {
+ throw t;
+ }
+ }
+
+ public static void testDropInsertArgumentsMultithreaded() throws Throwable {
+ MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "dropArguments_delegate",
+ MethodType.methodType(void.class, new Class<?>[]{String.class, long.class}));
+ MethodHandle mh = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
+ mh = MethodHandles.insertArguments(mh, 0, 3333, "bogon", "foo", 42);
+ invokeMultiThreaded(mh);
+ }
+
+ private static void exceptionHandler_delegate(NumberFormatException e, int x, int y, long z)
+ throws Throwable {
+ assertEquals(e.getClass(), NumberFormatException.class);
+ assertEquals(e.getMessage(), "fake");
+ assertEquals(x, 66);
+ assertEquals(y, 51);
+ assertEquals(z, 20000000000l);
+ }
+
+ public static void testThrowCatchExceptionMultiThreaded() throws Throwable {
+ MethodHandle thrower = MethodHandles.throwException(void.class,
+ NumberFormatException.class);
+ thrower = MethodHandles.dropArguments(thrower, 0, int.class, int.class, long.class);
+ MethodHandle handler = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "exceptionHandler_delegate",
+ MethodType.methodType(void.class, NumberFormatException.class,
+ int.class, int.class, long.class));
+ MethodHandle catcher =
+ MethodHandles.catchException(thrower, NumberFormatException.class, handler);
+ MethodHandle caller = MethodHandles.insertArguments(catcher, 0, 66, 51, 20000000000l,
+ new NumberFormatException("fake"));
+ invokeMultiThreaded(caller);
+ }
+
+ private static void testTargetAndFallback_delegate(MethodHandle mh) throws Throwable {
+ String actual = (String) mh.invoke("target", 42, 56);
+ assertEquals("target", actual);
+ actual = (String) mh.invoke("blah", 41, 56);
+ assertEquals("fallback", actual);
+ }
+
+ public static void testGuardWithTestMultiThreaded() throws Throwable {
+ MethodHandle test =
+ MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "testGuardWithTest_test",
+ MethodType.methodType(boolean.class,
+ new Class<?>[]{String.class,
+ long.class}));
+ final MethodType type = MethodType.methodType(String.class,
+ new Class<?>[]{String.class, long.class, int.class});
+ final MethodHandle target =
+ MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "testGuardWithTest_target", type);
+ final MethodHandle fallback =
+ MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+ "testGuardWithTest_fallback", type);
+ MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
+ MethodHandle tester = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class,
+ "testTargetAndFallback_delegate",
+ MethodType.methodType(void.class, MethodHandle.class));
+ invokeMultiThreaded(MethodHandles.insertArguments(tester, 0, adapter));
+ }
+
+ private static void arrayElementSetterGetter_delegate(MethodHandle getter,
+ MethodHandle setter,
+ int [] values)
+ throws Throwable{
+ for (int i = 0; i < values.length; ++i) {
+ int value = i * 13;
+ setter.invoke(values, i, value);
+ assertEquals(values[i], value);
+ assertEquals(getter.invoke(values, i), values[i]);
+ }
+ }
+
+ public static void testReferenceArrayGetterMultiThreaded() throws Throwable {
+ MethodHandle getter = MethodHandles.arrayElementGetter(int[].class);
+ MethodHandle setter = MethodHandles.arrayElementSetter(int[].class);
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class,
+ "arrayElementSetterGetter_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, int[].class));
+ mh = MethodHandles.insertArguments(mh, 0, getter, setter,
+ new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 });
+ invokeMultiThreaded(mh);
+ }
+
+ private static void checkConstant_delegate(MethodHandle mh, double value) throws Throwable {
+ assertEquals(mh.invoke(), value);
+ }
+
+ public static void testConstantMultithreaded() throws Throwable {
+ final double value = 7.77e77;
+ MethodHandle constant = MethodHandles.constant(double.class, value);
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "checkConstant_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, double.class));
+ mh = MethodHandles.insertArguments(mh, 0, constant, value);
+ invokeMultiThreaded(mh);
+ }
+
+ private static void checkIdentity_delegate(MethodHandle mh, char value) throws Throwable {
+ assertEquals(mh.invoke(value), value);
+ }
+
+ public static void testIdentityMultiThreaded() throws Throwable {
+ final char value = 'z';
+ MethodHandle identity = MethodHandles.identity(char.class);
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "checkIdentity_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, char.class));
+ mh = MethodHandles.insertArguments(mh, 0, identity, value);
+ invokeMultiThreaded(mh);
+ }
+
+ private static int multiplyByTwo(int x) { return x * 2; }
+ private static int divideByTwo(int x) { return x / 2; }
+ private static void assertMethodHandleInvokeEquals(MethodHandle mh, int value) throws Throwable{
+ assertEquals(mh.invoke(value), value);
+ }
+
+ public static void testFilterReturnValueMultiThreaded() throws Throwable {
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "multiplyByTwo",
+ MethodType.methodType(int.class, int.class));
+ MethodHandle filter = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "divideByTwo",
+ MethodType.methodType(int.class, int.class));
+ MethodHandle filtered = MethodHandles.filterReturnValue(target, filter);
+ assertEquals(filtered.invoke(33), 33);
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "assertMethodHandleInvokeEquals",
+ MethodType.methodType(void.class, MethodHandle.class, int.class));
+ invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, filtered, 77));
+ }
+
+ public static void compareStringAndFloat(String s, float f) {
+ assertEquals(s, Float.toString(f));
+ }
+
+ public static void testPermuteArgumentsMultiThreaded() throws Throwable {
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "compareStringAndFloat",
+ MethodType.methodType(void.class, String.class, float.class));
+ mh = MethodHandles.permuteArguments(
+ mh, MethodType.methodType(void.class, float.class, String.class), 1, 0);
+ invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, 2.22f, "2.22"));
+ }
+
+ public static void testSpreadInvokerMultiThreaded() throws Throwable {
+ MethodType methodType = MethodType.methodType(
+ int.class, new Class<?>[]{String.class, String.class, String.class});
+ MethodHandle delegate = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "spreadReferences", methodType);
+ MethodHandle mh = delegate.asSpreader(String[].class, 3);
+ mh = MethodHandles.insertArguments(mh, 0, new Object[] { new String [] { "a", "b", "c" }});
+ invokeMultiThreaded(mh);
+ }
+
+ public static void testCollectorMultiThreaded() throws Throwable {
+ MethodHandle trailingRef = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "collectCharSequence",
+ MethodType.methodType(int.class, String.class, CharSequence[].class));
+ MethodHandle mh = trailingRef.asCollector(String[].class, 2);
+ mh = MethodHandles.insertArguments(mh, 0, "a", "b", "c");
+ invokeMultiThreaded(mh);
+ }
+
+ public static void testFilterArgumentsMultiThreaded() throws Throwable {
+ MethodHandle filter1 = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "filter1",
+ MethodType.methodType(String.class, char.class));
+ MethodHandle filter2 = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "filter2",
+ MethodType.methodType(char.class, String.class));
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "filterTarget",
+ MethodType.methodType(int.class, String.class, char.class, String.class, char.class));
+ MethodHandle adapter = MethodHandles.filterArguments(target, 2, filter1, filter2);
+ invokeMultiThreaded(MethodHandles.insertArguments(adapter, 0, "a", 'b', 'c', "dXXXXX"));
+ }
+
+ private static void checkStringResult_delegate(MethodHandle mh,
+ String expected) throws Throwable {
+ assertEquals(mh.invoke(), expected);
+ }
+
+ public static void testCollectArgumentsMultiThreaded() throws Throwable {
+ MethodHandle filter = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "filter",
+ MethodType.methodType(String.class, char.class, char.class));
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "target",
+ MethodType.methodType(String.class, String.class, String.class, String.class));
+ MethodHandle collect = MethodHandles.collectArguments(target, 2, filter);
+ collect = MethodHandles.insertArguments(collect, 0, "a", "b", 'c', 'd');
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "checkStringResult_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, String.class));
+ invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, collect, "a: a, b: b, c: c+d"));
+ }
+
+ public static void testFoldArgumentsMultiThreaded() throws Throwable {
+ MethodHandle target = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "foldTarget",
+ MethodType.methodType(String.class, String.class,
+ char.class, char.class, String.class));
+ MethodHandle filter = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "foldFilter",
+ MethodType.methodType(String.class, char.class, char.class));
+ MethodHandle adapter = MethodHandles.foldArguments(target, filter);
+ adapter = MethodHandles.insertArguments(adapter, 0, 'c', 'd', "e");
+ MethodHandle mh = MethodHandles.lookup().findStatic(
+ MethodHandleCombinersTest.class, "checkStringResult_delegate",
+ MethodType.methodType(void.class, MethodHandle.class, String.class));
+ invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, adapter, "a: c+d ,b:c ,c:d ,d:e"));
+ }
}
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
index 8381cbd..9b3850c 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
@@ -412,18 +412,26 @@
public String bar();
}
- public static class BarSuper {
+ public static abstract class BarAbstractSuper {
+ public abstract String abstractSuperPublicMethod();
+ }
+
+ public static class BarSuper extends BarAbstractSuper {
public String superPublicMethod() {
return "superPublicMethod";
}
- public String superProtectedMethod() {
+ protected String superProtectedMethod() {
return "superProtectedMethod";
}
String superPackageMethod() {
return "superPackageMethod";
}
+
+ public String abstractSuperPublicMethod() {
+ return "abstractSuperPublicMethod";
+ }
}
public static class BarImpl extends BarSuper implements Bar {
@@ -506,7 +514,6 @@
BarImpl.class, "add",
MethodType.methodType(String.class, Integer.class, int.class));
} catch (NoSuchMethodException expected) {
-
}
// .. and their super-interfaces.
@@ -515,13 +522,16 @@
str = (String) mh.invoke(new BarImpl());
assertEquals("bar", str);
- // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the
- // invoke resulting in a failing check in the interpreter.
- //
- // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
- // MethodType.methodType(String.class));
- // str = (String) mh.invoke(new BarImpl());
- // assertEquals("bar", str);
+
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ assertEquals("bar", str);
+
+ mh = MethodHandles.lookup().findVirtual(BarAbstractSuper.class, "abstractSuperPublicMethod",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ assertEquals("abstractSuperPublicMethod", str);
// We should also be able to lookup public / protected / package methods in
// the super class, given sufficient access privileges.
@@ -548,7 +558,7 @@
}
}
- public static void testfindStatic() throws Throwable {
+ public void testfindStatic() throws Throwable {
MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod",
MethodType.methodType(String.class));
try {
@@ -580,7 +590,13 @@
}
}
- static class UnreflectTester {
+ static class UnreflectTesterBase {
+ public String overridenMethod() {
+ return "Base";
+ }
+ }
+
+ static class UnreflectTester extends UnreflectTesterBase {
public String publicField;
private String privateField;
@@ -617,9 +633,16 @@
public String publicVarArgsMethod(String... args) {
return "publicVarArgsMethod";
}
+
+ @Override
+ public String overridenMethod() {
+ return "Override";
+ }
+
+ public static final Lookup lookup = MethodHandles.lookup();
}
- public static void testUnreflects_publicMethods() throws Throwable {
+ public void testUnreflects_publicMethods() throws Throwable {
UnreflectTester instance = new UnreflectTester("unused");
Method publicMethod = UnreflectTester.class.getMethod("publicMethod");
@@ -633,7 +656,7 @@
assertEquals("publicStaticMethod", (String) mh.invokeExact());
}
- public static void testUnreflects_privateMethods() throws Throwable {
+ public void testUnreflects_privateMethods() throws Throwable {
Method privateMethod = UnreflectTester.class.getDeclaredMethod("privateMethod");
try {
@@ -642,10 +665,14 @@
} catch (IllegalAccessException expected) {
}
- privateMethod.setAccessible(true);
-
- MethodHandle mh = MethodHandles.lookup().unreflect(privateMethod);
UnreflectTester instance = new UnreflectTester("unused");
+ MethodHandle mh = UnreflectTester.lookup.unreflectSpecial(privateMethod,
+ UnreflectTester.class);
+ assertEquals("privateMethod", (String) mh.invoke(instance));
+ assertEquals("privateMethod", (String) mh.invokeExact(instance));
+
+ privateMethod.setAccessible(true);
+ mh = MethodHandles.lookup().unreflect(privateMethod);
assertEquals("privateMethod", (String) mh.invoke(instance));
assertEquals("privateMethod", (String) mh.invokeExact(instance));
@@ -656,13 +683,28 @@
} catch (IllegalAccessException expected) {
}
+ try {
+ mh = UnreflectTester.lookup.unreflectSpecial(privateStaticMethod,
+ UnreflectTester.class);
+ fail();
+ } catch (IllegalAccessException expected) {
+ }
+
privateStaticMethod.setAccessible(true);
mh = MethodHandles.lookup().unreflect(privateStaticMethod);
assertEquals("privateStaticMethod", (String) mh.invoke());
assertEquals("privateStaticMethod", (String) mh.invokeExact());
}
- public static void testUnreflects_constructors() throws Throwable {
+ public void testUnreflectSpecial_superCalls() throws Throwable {
+ Method overridenMethod = UnreflectTester.class.getMethod("overridenMethod");
+ UnreflectTester instance = new UnreflectTester("unused");
+ MethodHandle mh = UnreflectTester.lookup.unreflectSpecial(overridenMethod,
+ UnreflectTester.class);
+ assertEquals("Base", (String) mh.invoke(instance));
+ }
+
+ public void testUnreflects_constructors() throws Throwable {
Constructor privateConstructor = UnreflectTester.class.getDeclaredConstructor(String.class);
try {
@@ -686,7 +728,7 @@
assertEquals("def", instance.publicField);
}
- public static void testUnreflects_publicFields() throws Throwable {
+ public void testUnreflects_publicFields() throws Throwable {
Field publicField = UnreflectTester.class.getField("publicField");
MethodHandle mh = MethodHandles.lookup().unreflectGetter(publicField);
UnreflectTester instance = new UnreflectTester("instanceValue");
@@ -708,7 +750,7 @@
assertEquals("updatedStaticValue2", UnreflectTester.publicStaticField);
}
- public static void testUnreflects_privateFields() throws Throwable {
+ public void testUnreflects_privateFields() throws Throwable {
Field privateField = UnreflectTester.class.getDeclaredField("privateField");
try {
MethodHandles.lookup().unreflectGetter(privateField);
@@ -760,7 +802,7 @@
return "foo";
}
- public static void testAsType() throws Throwable {
+ public void testAsType() throws Throwable {
// The type of this handle is (String, String)String.
MethodHandle mh = MethodHandles.lookup().findVirtual(String.class,
"concat", MethodType.methodType(String.class, String.class));
@@ -803,7 +845,7 @@
}
}
- public static void testConstructors() throws Throwable {
+ public void testConstructors() throws Throwable {
MethodHandle mh =
MethodHandles.lookup().findConstructor(Float.class,
MethodType.methodType(void.class,
@@ -872,7 +914,7 @@
}
}
- public static void testStringConstructors() throws Throwable {
+ public void testStringConstructors() throws Throwable {
final String testPattern = "The system as we know it is broken";
// String()
@@ -965,7 +1007,7 @@
assertEquals(testPattern, s);
}
- private static void testReferenceReturnValueConversions() throws Throwable {
+ public void testReferenceReturnValueConversions() throws Throwable {
MethodHandle mh = MethodHandles.lookup().findStatic(
Float.class, "valueOf", MethodType.methodType(Float.class, String.class));
@@ -1023,7 +1065,7 @@
assertEquals(0, c.compareTo(Float.valueOf(2.125f)));
}
- private static void testPrimitiveReturnValueConversions() throws Throwable {
+ public void testPrimitiveReturnValueConversions() throws Throwable {
MethodHandle mh = MethodHandles.lookup().findStatic(
Math.class, "min", MethodType.methodType(int.class, int.class, int.class));
@@ -1155,11 +1197,6 @@
}
}
- public static void testReturnValueConversions() throws Throwable {
- testReferenceReturnValueConversions();
- testPrimitiveReturnValueConversions();
- }
-
public static class BaseVariableArityTester {
public String update(Float f0, Float... floats) {
return "base " + f0 + ", " + Arrays.toString(floats);
@@ -1860,6 +1897,72 @@
assertEquals(MethodType.methodType(String.class), info.getMethodType());
}
+ public void testReflectAs() throws Throwable {
+ // Test with a virtual method :
+ MethodType type = MethodType.methodType(String.class);
+ MethodHandle handle = MethodHandles.lookup().findVirtual(
+ UnreflectTester.class, "publicMethod", type);
+
+ Method reflected = MethodHandles.reflectAs(Method.class, handle);
+ Method meth = UnreflectTester.class.getMethod("publicMethod");
+ assertEquals(meth, reflected);
+
+ try {
+ MethodHandles.reflectAs(Field.class, handle);
+ fail();
+ } catch (ClassCastException expected) {
+ }
+
+ try {
+ MethodHandles.reflectAs(Constructor.class, handle);
+ fail();
+ } catch (ClassCastException expected) {
+ }
+
+ // Test with a private instance method, unlike the "checked crack" (lol..) API exposed
+ // by revealDirect, this doesn't perform any access checks.
+ handle = UnreflectTester.lookup.findSpecial(
+ UnreflectTester.class, "privateMethod", type, UnreflectTester.class);
+ meth = UnreflectTester.class.getDeclaredMethod("privateMethod");
+ reflected = MethodHandles.reflectAs(Method.class, handle);
+ assertEquals(meth, reflected);
+
+ // Test with a constructor :
+ type = MethodType.methodType(void.class, String.class, boolean.class);
+ handle = MethodHandles.lookup().findConstructor(UnreflectTester.class, type);
+
+ Constructor cons = UnreflectTester.class.getConstructor(String.class, boolean.class);
+ Constructor reflectedCons = MethodHandles.reflectAs(Constructor.class, handle);
+ assertEquals(cons, reflectedCons);
+
+ try {
+ MethodHandles.reflectAs(Method.class, handle);
+ fail();
+ } catch (ClassCastException expected) {
+ }
+
+ // Test with an instance field :
+ handle = MethodHandles.lookup().findSetter(
+ UnreflectTester.class, "publicField", String.class);
+
+ Field field = UnreflectTester.class.getField("publicField");
+ Field reflectedField = MethodHandles.reflectAs(Field.class, handle);
+ assertEquals(field, reflectedField);
+
+ try {
+ MethodHandles.reflectAs(Method.class, handle);
+ fail();
+ } catch (ClassCastException expected) {
+ }
+
+ // Test with a non-direct method handle.
+ try {
+ MethodHandles.reflectAs(Method.class, MethodHandles.constant(String.class, "foo"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
public static class Inner1 {
public static MethodHandles.Lookup lookup = MethodHandles.lookup();
}
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index f8a905e..73c6ea3 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -16,24 +16,35 @@
package libcore.java.net;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
-import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
import libcore.util.SerializationTester;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class InetAddressTest extends junit.framework.TestCase {
+@RunWith(JUnitParamsRunner.class)
+public class InetAddressTest {
private static final byte[] LOOPBACK4_BYTES = new byte[] { 127, 0, 0, 1 };
private static final byte[] LOOPBACK6_BYTES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
- private static final String[] INVALID_IPv4_NUMERIC_ADDRESSES = new String[] {
+ private static final String[] INVALID_IPv4_AND_6_NUMERIC_ADDRESSES = new String[] {
// IPv4 addresses may not be surrounded by square brackets.
"[127.0.0.1]",
@@ -55,6 +66,9 @@
"1234",
"0", // Single out the deprecated form of the ANY address.
+ // Older Harmony tests expected this to be resolved to 255.255.255.255.
+ "4294967295", // 0xffffffffL,
+
// Hex. Not supported by Android but supported by the RI.
"0x1.0x2.0x3.0x4",
"0x7f.0x00.0x00.0x01",
@@ -71,6 +85,25 @@
"1.-1.0.1",
"1.0.-1.1",
"1.0.0.-1",
+
+ // Invalid IPv6 addresses
+ "FFFF:FFFF",
+ };
+
+ private static final String VALID_IPv6_ADDRESSES[] = {
+ "::1.2.3.4",
+ "::",
+ "::",
+ "1::0",
+ "1::",
+ "::1",
+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255",
+ "0:0:0:0:0:0:0:0",
+ "0:0:0:0:0:0:0.0.0.0",
+ "::255.255.255.255",
+ "::FFFF:0.0.0.0",
+ "F:F:F:F:F:F:F:F",
};
private static Inet6Address loopback6() throws Exception {
@@ -81,66 +114,96 @@
return (Inet6Address) InetAddress.getByAddress("ip6-localhost", LOOPBACK6_BYTES);
}
- public void test_parseNumericAddress() throws Exception {
- // Regular IPv4.
- assertEquals("/1.2.3.4", InetAddress.parseNumericAddress("1.2.3.4").toString());
- // Regular IPv6.
- assertEquals("/2001:4860:800d::68", InetAddress.parseNumericAddress("2001:4860:800d::68").toString());
- // Mapped IPv4
- assertEquals("/127.0.0.1", InetAddress.parseNumericAddress("::ffff:127.0.0.1").toString());
- // Optional square brackets around IPv6 addresses, including mapped IPv4.
- assertEquals("/2001:4860:800d::68", InetAddress.parseNumericAddress("[2001:4860:800d::68]").toString());
- assertEquals("/127.0.0.1", InetAddress.parseNumericAddress("[::ffff:127.0.0.1]").toString());
+ public static String[][] validNumericAddressesAndStringRepresentation() {
+ return new String[][]{
+ // Regular IPv4.
+ { "1.2.3.4", "/1.2.3.4" },
+ // Regular IPv6.
+ { "2001:4860:800d::68", "/2001:4860:800d::68" },
+
+ // Mapped IPv4
+ { "::ffff:127.0.0.1", "/127.0.0.1" },
+
+ // Optional square brackets around IPv6 addresses, including mapped IPv4.
+ { "[2001:4860:800d::68]", "/2001:4860:800d::68" },
+ { "[::ffff:127.0.0.1]", "/127.0.0.1" },
+
+ // Android does not recognize Octal (leading 0) cases: they are treated as decimal.
+ { "0177.00.00.01", "/177.0.0.1" },
+ };
+ }
+
+ @Parameters(method = "validNumericAddressesAndStringRepresentation")
+ @Test
+ public void test_parseNumericAddress(String address, String expectedString) throws Exception {
+ assertEquals(expectedString, InetAddress.parseNumericAddress(address).toString());
+ }
+
+ @Test
+ public void test_parseNumericAddress_notNumeric() throws Exception {
try {
InetAddress.parseNumericAddress("example.com"); // Not numeric.
fail();
} catch (IllegalArgumentException expected) {
}
- // Android does not recognize Octal (leading 0) cases: they are treated as decimal.
- assertEquals("/177.0.0.1", InetAddress.parseNumericAddress("0177.00.00.01").toString());
-
- for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) {
- try {
- InetAddress.parseNumericAddress(invalid);
- fail(invalid);
- } catch (IllegalArgumentException expected) {
- }
- }
-
// Strange special cases, for compatibility with InetAddress.getByName.
assertTrue(InetAddress.parseNumericAddress(null).isLoopbackAddress());
assertTrue(InetAddress.parseNumericAddress("").isLoopbackAddress());
}
- public void test_isNumeric() throws Exception {
- // IPv4
- assertTrue(InetAddress.isNumeric("1.2.3.4"));
- assertTrue(InetAddress.isNumeric("127.0.0.1"));
-
- // IPv6
- assertTrue(InetAddress.isNumeric("::1"));
- assertTrue(InetAddress.isNumeric("2001:4860:800d::68"));
-
- // Mapped IPv4
- assertTrue(InetAddress.isNumeric("::ffff:127.0.0.1"));
-
- // Optional square brackets around IPv6 addresses, including mapped IPv4.
- assertTrue(InetAddress.isNumeric("[2001:4860:800d::68]"));
- assertTrue(InetAddress.isNumeric("[::ffff:127.0.0.1]"));
-
- // Negative test
- assertFalse(InetAddress.isNumeric("example.com"));
-
- // Android does not handle Octal (leading 0) cases: they are treated as decimal.
- assertTrue(InetAddress.isNumeric("0177.00.00.01")); // Interpreted as 177.0.0.1
-
- for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) {
- assertFalse(invalid, InetAddress.isNumeric(invalid));
+ @Parameters(method = "invalidNumericAddresses")
+ @Test
+ public void test_parseNumericAddress_invalid(String invalid) throws Exception {
+ try {
+ InetAddress.parseNumericAddress(invalid);
+ fail(invalid);
+ } catch (IllegalArgumentException expected) {
}
}
+ public static String[] validNumericAddresses() {
+ return new String[] {
+ // IPv4
+ "1.2.3.4",
+ "127.0.0.1",
+
+ // IPv6
+ "::1",
+ "2001:4860:800d::68",
+
+ // Mapped IPv4
+ "::ffff:127.0.0.1",
+
+ // Optional square brackets around IPv6 addresses, including mapped IPv4.
+ "[2001:4860:800d::68]",
+ "[::ffff:127.0.0.1]",
+
+ // Android does not handle Octal (leading 0) cases: they are treated as decimal.
+ "0177.00.00.01",
+ };
+ }
+
+ @Parameters(method = "validNumericAddresses")
+ @Test
+ public void test_isNumeric(String valid) throws Exception {
+ assertTrue(InetAddress.isNumeric(valid));
+ }
+
+ @Test
+ public void test_isNumeric_notNumeric() throws Exception {
+ // Negative test
+ assertFalse(InetAddress.isNumeric("example.com"));
+ }
+
+ @Parameters(method = "invalidNumericAddresses")
+ @Test
+ public void test_isNumeric_invalid(String invalid) {
+ assertFalse(invalid, InetAddress.isNumeric(invalid));
+ }
+
+ @Test
public void test_isLinkLocalAddress() throws Exception {
assertFalse(InetAddress.getByName("127.0.0.1").isLinkLocalAddress());
assertFalse(InetAddress.getByName("::ffff:127.0.0.1").isLinkLocalAddress());
@@ -150,6 +213,7 @@
assertTrue(InetAddress.getByName("fe80::").isLinkLocalAddress());
}
+ @Test
public void test_isMCSiteLocalAddress() throws Exception {
assertFalse(InetAddress.getByName("239.254.255.255").isMCSiteLocal());
assertTrue(InetAddress.getByName("239.255.0.0").isMCSiteLocal());
@@ -161,6 +225,7 @@
assertTrue(InetAddress.getByName("ff15::").isMCSiteLocal());
}
+ @Test
public void test_isReachable() throws Exception {
// http://code.google.com/p/android/issues/detail?id=20203
String s = "aced0005737200146a6176612e6e65742e496e6574416464726573732d9b57af"
@@ -182,6 +247,7 @@
}.test();
}
+ @Test
public void test_isReachable_neverThrows() throws Exception {
InetAddress inetAddress = InetAddress.getByName("www.google.com");
@@ -197,6 +263,7 @@
// IPPROTO_ICMP socket kind requires setting ping_group_range. This is set on boot on Android.
// When running on host, make sure you run the command:
// sudo sysctl -w net.ipv4.ping_group_range="0 65535"
+ @Test
public void test_isReachable_by_ICMP() throws Exception {
InetAddress[] inetAddresses = InetAddress.getAllByName("www.google.com");
for (InetAddress ia : inetAddresses) {
@@ -209,6 +276,7 @@
assertFalse(blackholeAddress.isReachable(1000));
}
+ @Test
public void test_isSiteLocalAddress() throws Exception {
assertFalse(InetAddress.getByName("144.32.32.1").isSiteLocalAddress());
assertTrue(InetAddress.getByName("10.0.0.1").isSiteLocalAddress());
@@ -220,20 +288,53 @@
assertTrue(InetAddress.getByName("fec0::").isSiteLocalAddress());
}
- public void test_getByName() throws Exception {
- for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) {
- try {
- InetAddress.getByName(invalid);
- fail(invalid);
- } catch (UnknownHostException expected) {
- }
+ public static String[] invalidNumericAddresses() {
+ return INVALID_IPv4_AND_6_NUMERIC_ADDRESSES;
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ @Parameters(method = "invalidNumericAddresses")
+ @Test
+ public void test_getByName_invalid(String invalid) throws Exception {
+ try {
+ InetAddress.getByName(invalid);
+ fail("Invalid IP address incorrectly recognized as valid: "
+ + invalid);
+ } catch (UnknownHostException expected) {
+ }
+
+ // exercise negative cache
+ try {
+ InetAddress.getByName(invalid);
+ fail("Invalid IP address incorrectly recognized as valid: "
+ + invalid);
+ } catch (Exception expected) {
}
}
+ public static String[] validIPv6Addresses() {
+ return VALID_IPv6_ADDRESSES;
+ }
+
+ @Parameters(method = "validIPv6Addresses")
+ @Test
+ public void test_getByName_valid(String valid) throws Exception {
+ InetAddress.getByName(valid);
+
+ // exercise positive cache
+ InetAddress.getByName(valid);
+
+ // when wrapped in [..]
+ String tempIPAddress = "[" + valid + "]";
+ InetAddress.getByName(tempIPAddress);
+ }
+
+ @Test
public void test_getLoopbackAddress() throws Exception {
assertTrue(InetAddress.getLoopbackAddress().isLoopbackAddress());
}
+ @Test
public void test_equals() throws Exception {
InetAddress addr = InetAddress.getByName("239.191.255.255");
assertTrue(addr.equals(addr));
@@ -247,6 +348,7 @@
assertEquals(Inet6Address.getByAddress("1", bs, 1), Inet6Address.getByAddress("2", bs, 2));
}
+ @Test
public void test_getHostAddress() throws Exception {
assertEquals("::1", localhost6().getHostAddress());
assertEquals("::1", InetAddress.getByName("::1").getHostAddress());
@@ -314,6 +416,7 @@
assertEquals("10:2030:4050:6070:8090:a0b0:c0d0:e0f0", aAddr.getHostAddress());
}
+ @Test
public void test_hashCode() throws Exception {
InetAddress addr1 = InetAddress.getByName("1.0.0.1");
InetAddress addr2 = InetAddress.getByName("1.0.0.1");
@@ -322,29 +425,30 @@
assertTrue(loopback6().hashCode() == localhost6().hashCode());
}
- public void test_toString() throws Exception {
- String validIPAddresses[] = {
- "::1.2.3.4", "::", "::", "1::0", "1::", "::1",
- "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
- "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255",
- "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0.0.0.0"
+ public static String[][] validAddressesAndStringRepresentation() {
+ return new String[][] {
+ { "::1.2.3.4", "/::1.2.3.4" },
+ { "::", "/::" },
+ { "1::0", "/1::" },
+ { "1::", "/1::" },
+ { "::1", "/::1" },
+ { "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" },
+ { "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255", "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" },
+ { "0:0:0:0:0:0:0:0", "/::" },
+ { "0:0:0:0:0:0:0.0.0.0", "/::" },
};
-
- String [] resultStrings = {
- "/::1.2.3.4", "/::", "/::", "/1::", "/1::", "/::1",
- "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
- "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "/::",
- "/::"
- };
-
- for(int i = 0; i < validIPAddresses.length; i++) {
- InetAddress ia = InetAddress.getByName(validIPAddresses[i]);
- String result = ia.toString();
- assertNotNull(result);
- assertEquals(resultStrings[i], result);
- }
}
+ @Parameters(method = "validAddressesAndStringRepresentation")
+ @Test
+ public void test_toString(String address, String expectedString) throws Exception {
+ InetAddress ia = InetAddress.getByName(address);
+ String result = ia.toString();
+ assertNotNull(result);
+ assertEquals(expectedString, result);
+ }
+
+ @Test
public void test_getHostNameCaches() throws Exception {
InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
@@ -358,52 +462,60 @@
assertEquals("ip6-localhost", getHostStringWithoutReverseDns(inetAddress));
}
+ @Test
public void test_getByAddress_loopbackIpv4() throws Exception {
InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK4_BYTES);
- assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
+ @Test
public void test_getByAddress_loopbackIpv6() throws Exception {
InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
- assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
+ @Test
public void test_getByName_loopbackIpv4() throws Exception {
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
- assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
+ @Test
public void test_getByName_loopbackIpv6() throws Exception {
InetAddress inetAddress = InetAddress.getByName("::1");
- assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
+ @Test
public void test_getByName_empty() throws Exception {
InetAddress inetAddress = InetAddress.getByName("");
- assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
+ @Test
public void test_getAllByName_localhost() throws Exception {
InetAddress[] inetAddresses = InetAddress.getAllByName("localhost");
assertEquals(1, inetAddresses.length);
InetAddress inetAddress = inetAddresses[0];
- assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
+ @Test
public void test_getAllByName_ip6_localhost() throws Exception {
InetAddress[] inetAddresses = InetAddress.getAllByName("ip6-localhost");
assertEquals(1, inetAddresses.length);
InetAddress inetAddress = inetAddresses[0];
- assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
assertTrue(inetAddress.isLoopbackAddress());
}
+ @Test
public void test_getByName_v6loopback() throws Exception {
InetAddress inetAddress = InetAddress.getByName("::1");
@@ -412,6 +524,7 @@
assertTrue(expectedLoopbackAddresses.contains(inetAddress));
}
+ @Test
public void test_getByName_cloning() throws Exception {
InetAddress[] addresses = InetAddress.getAllByName(null);
InetAddress[] addresses2 = InetAddress.getAllByName(null);
@@ -427,6 +540,7 @@
assertNotNull(addresses2[1]);
}
+ @Test
public void test_getAllByName_null() throws Exception {
InetAddress[] inetAddresses = InetAddress.getAllByName(null);
assertEquals(2, inetAddresses.length);
@@ -436,13 +550,14 @@
}
// http://b/29311351
+ @Test
public void test_loopbackConstantsPreInitializedNames() {
// Note: Inet6Address / Inet4Address equals() does not check host name.
assertEquals("ip6-localhost", getHostStringWithoutReverseDns(Inet6Address.LOOPBACK));
assertEquals("localhost", getHostStringWithoutReverseDns(Inet4Address.LOOPBACK));
}
- private static void assertEquals(
+ private static void checkInetAddress(
byte[] expectedAddressBytes, String expectedHostname, InetAddress actual) {
assertArrayEquals(expectedAddressBytes, actual.getAddress());
assertEquals(expectedHostname, actual.getHostName());
diff --git a/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
index b21689e..9515fcc 100644
--- a/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
+++ b/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
@@ -96,16 +96,17 @@
fail("Abort: " + e);
throw new AssertionError("unreachable");
}
- final CountDownLatch shutdownLatch = new CountDownLatch(1);
- ServerRunnable serverRunnable = new ServerRunnable(serverSocket, shutdownLatch);
+ ServerRunnable serverRunnable = new ServerRunnable(serverSocket);
Thread serverThread = new Thread(serverRunnable, TAG + " (server)");
ClientRunnable clientRunnable = new ClientRunnable(
- serverSocket.getLocalSocketAddress(), shutdownLatch);
+ serverSocket.getLocalSocketAddress(), serverRunnable);
Thread clientThread = new Thread(clientRunnable, TAG + " (client)");
serverThread.start();
clientThread.start();
try {
- if (shutdownLatch.getCount() == 0) {
+ assertTrue("Slow server startup", serverRunnable.awaitStart(1, TimeUnit.SECONDS));
+ assertTrue("Slow client startup", clientRunnable.awaitStart(1, TimeUnit.SECONDS));
+ if (serverRunnable.isShutdown()) {
fail("Server prematurely shut down");
}
// Let server and client keep connecting for some time, then close the socket.
@@ -117,9 +118,8 @@
}
// Check that the server shut down quickly in response to the socket closing.
long hardLimitSeconds = 5;
- boolean serverShutdownReached = shutdownLatch.await(hardLimitSeconds, TimeUnit.SECONDS);
+ boolean serverShutdownReached = serverRunnable.awaitShutdown(hardLimitSeconds, TimeUnit.SECONDS);
if (!serverShutdownReached) { // b/27763633
- shutdownLatch.countDown();
String serverStackTrace = stackTraceAsString(serverThread.getStackTrace());
fail("Server took > " + hardLimitSeconds + "sec to react to serverSocket.close(). "
+ "Server thread's stackTrace: " + serverStackTrace);
@@ -134,7 +134,7 @@
iterationName, msecPerIteration),
serverRunnable.numSuccessfulConnections > 0);
- assertEquals(0, shutdownLatch.getCount());
+ assertTrue(serverRunnable.isShutdown());
// Sanity check to ensure the threads don't live into the next iteration. This should
// be quick because we only get here if shutdownLatch reached 0 within the time limit.
serverThread.join();
@@ -151,17 +151,20 @@
*/
static class ClientRunnable implements Runnable {
private final SocketAddress socketAddress;
- private final CountDownLatch shutdownLatch;
+
+ private final ServerRunnable serverRunnable;
+ private final CountDownLatch startLatch = new CountDownLatch(1);
public ClientRunnable(
- SocketAddress socketAddress, CountDownLatch shutdownLatch) {
+ SocketAddress socketAddress, ServerRunnable serverRunnable) {
this.socketAddress = socketAddress;
- this.shutdownLatch = shutdownLatch;
+ this.serverRunnable = serverRunnable;
}
@Override
public void run() {
- while (shutdownLatch.getCount() != 0) { // check if server is shutting down
+ startLatch.countDown();
+ while (!serverRunnable.isShutdown()) {
try {
Socket socket = new Socket();
socket.connect(socketAddress, /* timeout (msec) */ 10);
@@ -171,6 +174,11 @@
}
}
}
+
+ public boolean awaitStart(long timeout, TimeUnit timeUnit) throws InterruptedException {
+ return startLatch.await(timeout, timeUnit);
+ }
+
}
/**
@@ -180,15 +188,16 @@
static class ServerRunnable implements Runnable {
private final ServerSocket serverSocket;
volatile int numSuccessfulConnections;
- private final CountDownLatch shutdownLatch;
+ private final CountDownLatch startLatch = new CountDownLatch(1);
+ private final CountDownLatch shutdownLatch = new CountDownLatch(1);
- ServerRunnable(ServerSocket serverSocket, CountDownLatch shutdownLatch) {
+ ServerRunnable(ServerSocket serverSocket) {
this.serverSocket = serverSocket;
- this.shutdownLatch = shutdownLatch;
}
@Override
public void run() {
+ startLatch.countDown();
int numSuccessfulConnections = 0;
while (true) {
try {
@@ -204,6 +213,18 @@
}
}
}
+
+ public boolean awaitStart(long timeout, TimeUnit timeUnit) throws InterruptedException {
+ return startLatch.await(timeout, timeUnit);
+ }
+
+ public boolean awaitShutdown(long timeout, TimeUnit timeUnit) throws InterruptedException {
+ return shutdownLatch.await(timeout, timeUnit);
+ }
+
+ public boolean isShutdown() {
+ return shutdownLatch.getCount() == 0;
+ }
}
private static String stackTraceAsString(StackTraceElement[] stackTraceElements) {
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index c80d2ec..750e73a 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -158,7 +158,20 @@
// allow (but strip) trailing \n, \r and \r\n
// assertForbiddenRequestHeaderValue("\r");
// End of workaround
- assertEquals("a valid\tvalue", setAndReturnRequestHeaderValue("a valid\tvalue"));
+
+ // '\t' in header values can either be (a) forbidden or (b) allowed.
+ // The original version of Android N (API 23) implemented behavior
+ // (a), but OEMs can backport a fix that changes the behavior to (b).
+ // Therefore, this test has been relaxed for Android N CTS to allow
+ // either behavior. It is planned that future versions of Android only
+ // allow behavior (b).
+ try {
+ // throws IAE in case (a), passes in case (b)
+ assertEquals("a valid\tvalue", setAndReturnRequestHeaderValue("a valid\tvalue"));
+ } catch (IllegalArgumentException tolerated) {
+ // verify case (a)
+ assertForbiddenRequestHeaderValue("\t");
+ }
assertForbiddenRequestHeaderValue("\u001f");
assertForbiddenRequestHeaderValue("\u007f");
diff --git a/luni/src/test/java/libcore/java/nio/BufferTest.java b/luni/src/test/java/libcore/java/nio/BufferTest.java
index 9063e27..c54d978 100644
--- a/luni/src/test/java/libcore/java/nio/BufferTest.java
+++ b/luni/src/test/java/libcore/java/nio/BufferTest.java
@@ -955,7 +955,7 @@
ByteBuffer b2 = b1.duplicate();
NioUtils.freeDirectBuffer(b1);
for (ByteBuffer b: new ByteBuffer[] { b1, b2 }) {
- //assertFalse(b.isAccessible());
+ assertFalse(b.isAccessible());
try {
b.compact();
fail();
@@ -973,7 +973,6 @@
}
}
- /* setAccessible is not available in OpenJdk's buffers.
public void testAccess() {
ByteBuffer b1 = ByteBuffer.allocate(1);
ByteBuffer b2 = b1.duplicate();
@@ -1057,7 +1056,7 @@
testAsMethods(b);
testGetMethods(b);
}
- }*/
+ }
private void testPutMethods(ByteBuffer b) {
b.position(0);
diff --git a/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java
new file mode 100644
index 0000000..905f120
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java
@@ -0,0 +1,683 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.nio.channels;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousFileChannel;
+import java.nio.channels.CompletionHandler;
+import java.nio.channels.NonReadableChannelException;
+import java.nio.channels.NonWritableChannelException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.*;
+
+@RunWith(JUnit4.class)
+public class AsynchronousFileChannelTest {
+
+ @Test
+ public void testOpen_create() throws Throwable {
+ Path tempDir = Files.createTempDirectory("ASFCTest_test_open_create");
+
+ Path newFile = tempDir.resolve("newFile");
+ AsynchronousFileChannel channel = AsynchronousFileChannel.open(newFile,
+ StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+ assertTrue(channel.isOpen());
+ assertEquals(0, channel.size());
+ channel.close();
+ }
+
+ @Test
+ public void testOpen_existing() throws Throwable {
+ // Demonstrates a few warts related to re-opening files that already exist.
+ Path tempDir = Files.createTempDirectory("ASFCTest_test_open_existing");
+ Path newFile = tempDir.resolve("newFile");
+
+ // Create a new file.
+ AsynchronousFileChannel channel = AsynchronousFileChannel.open(newFile,
+ StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+ channel.close();
+
+ // This should fail, but it doesn't..
+ AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE_NEW);
+ // ..unless it's paired with a write.
+ try {
+ AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE_NEW,
+ StandardOpenOption.WRITE);
+ fail();
+ } catch (FileAlreadyExistsException expected) {
+ }
+
+ // Create should still work, though.
+ channel = AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE,
+ StandardOpenOption.WRITE);
+ channel.close();
+ }
+
+ @Test
+ public void testOpen_nonexistent() throws Throwable {
+ Path tempDir = Files.createTempDirectory("ASFCTest_test_open_nonexistent");
+
+ Path nonExistent = tempDir.resolve("nonExistentFile");
+ try {
+ AsynchronousFileChannel.open(nonExistent, StandardOpenOption.READ);
+ fail();
+ } catch (NoSuchFileException expected) {
+ }
+
+ try {
+ AsynchronousFileChannel.open(nonExistent, StandardOpenOption.WRITE);
+ fail();
+ } catch (NoSuchFileException expected) {
+ }
+
+ // The following three cases haven't clearly been mentioned in the documentation.
+ // CREATE / CREATE_NEW fail unless they're paired with WRITE.
+ //
+ // CREATE will succeed without WRITE in the case that the file already exists, which
+ // seems like a wart.
+ try {
+ AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE);
+ fail();
+ } catch (NoSuchFileException expected) {
+ }
+
+ try {
+ AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE,
+ StandardOpenOption.READ);
+ fail();
+ } catch (NoSuchFileException expected) {
+ }
+
+ try {
+ AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE_NEW);
+ fail();
+ } catch (NoSuchFileException expected) {
+ }
+ }
+
+ private static File createTemporaryFile(int size) throws IOException {
+ if (size % 256 != 0) {
+ throw new IllegalArgumentException("size % 256 != 0: " + size);
+ }
+
+ File temp = File.createTempFile("AFCTest_tempfile", "");
+ byte[] buf = new byte[256];
+
+ try (FileOutputStream fos = new FileOutputStream(temp)) {
+ int bytesWritten = 0;
+ while (bytesWritten < size) {
+ fos.write(buf);
+ bytesWritten += buf.length;
+ }
+ }
+
+ return temp;
+ }
+
+ private static File createTemporaryFile(byte[] contents) throws IOException {
+ File temp = File.createTempFile("AFCTest_tempfile", "");
+
+ try (FileOutputStream fos = new FileOutputStream(temp)) {
+ fos.write(contents);
+ }
+
+ return temp;
+ }
+
+ @Test
+ public void testOpen_truncate() throws Throwable {
+ File temp = createTemporaryFile(256);
+ assertEquals(256, temp.length());
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
+ afc.close();
+
+ assertEquals(0, temp.length());
+ }
+
+ @Test
+ public void testOpen_deleteOnClose() throws Throwable {
+ File temp = createTemporaryFile(256);
+ assertTrue(temp.exists());
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.READ);
+ assertEquals(256, afc.size());
+ afc.close();
+
+ assertFalse(temp.exists());
+ }
+
+ @Test
+ public void testRead_Future() throws Throwable {
+ byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+ File temp = createTemporaryFile(contents);
+
+ byte[] readBuf = new byte[4];
+ ByteBuffer buf = ByteBuffer.wrap(readBuf);
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.WRITE);
+ try {
+ afc.read(buf, 0);
+ fail();
+ } catch (NonReadableChannelException exception) {
+ }
+
+ afc.close();
+
+ afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.READ);
+
+ Future<Integer> fut = afc.read(buf, 0);
+ assertEquals(4, (int) fut.get());
+ buf.flip();
+ assertEquals('a', readBuf[0]);
+ assertEquals('b', readBuf[1]);
+ assertEquals('c', readBuf[2]);
+ assertEquals('d', readBuf[3]);
+
+ // Short read: at the end of the file.
+ fut = afc.read(buf, 6);
+ assertEquals(2, (int) fut.get());
+ assertEquals('g', readBuf[0]);
+ assertEquals('h', readBuf[1]);
+
+ // Reads past the end of the file.
+ fut = afc.read(buf, 8);
+ assertEquals(-1, (int) fut.get());
+
+ fut = afc.read(buf, 9);
+ assertEquals(-1, (int) fut.get());
+
+ try {
+ afc.read(buf, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ afc.read(null, 1);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ afc.read(buf.asReadOnlyBuffer(), 1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ afc.close();
+ }
+
+ static class RecordingHandler implements CompletionHandler<Integer, String> {
+ public String attachment;
+ public int result;
+ public Throwable exc;
+
+ private final CountDownLatch cdl = new CountDownLatch(1);
+
+ @Override
+ public void completed(Integer result, String attachment) {
+ this.result = result;
+ this.attachment = attachment;
+
+ cdl.countDown();
+ }
+
+ @Override
+ public void failed(Throwable exc, String attachment) {
+ this.exc = exc;
+ this.attachment = attachment;
+ }
+
+ public boolean awaitCompletion() throws InterruptedException {
+ return cdl.await(10, TimeUnit.SECONDS);
+ }
+ }
+
+ @Test
+ public void testRead_CompletionListener() throws Throwable {
+ byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+ File temp = createTemporaryFile(contents);
+
+ byte[] readBuf = new byte[4];
+ ByteBuffer buf = ByteBuffer.wrap(readBuf);
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.WRITE);
+ String attachment = "ATTACHMENT";
+ RecordingHandler handler = new RecordingHandler();
+
+ try {
+ afc.read(buf, 0, attachment, handler);
+ fail();
+ } catch (NonReadableChannelException exception) {
+ }
+
+ afc.close();
+
+ afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.READ);
+ afc.read(buf, 0, attachment, handler);
+ assertTrue(handler.awaitCompletion());
+ assertEquals(4, handler.result);
+ assertSame(attachment, handler.attachment);
+ buf.flip();
+ assertEquals('a', readBuf[0]);
+ assertEquals('b', readBuf[1]);
+ assertEquals('c', readBuf[2]);
+ assertEquals('d', readBuf[3]);
+
+ // Short read: at the end of the file.
+ handler = new RecordingHandler();
+ attachment = "ATTACHMENT2";
+ afc.read(buf, 6, attachment, handler);
+ assertTrue(handler.awaitCompletion());
+ assertEquals(2, handler.result);
+ assertSame(attachment, handler.attachment);
+ assertEquals('g', readBuf[0]);
+ assertEquals('h', readBuf[1]);
+
+ // Reads past the end of the file.
+ handler = new RecordingHandler();
+ attachment = "ATTACHMENT3";
+ afc.read(buf, 8, attachment, handler);
+ assertTrue(handler.awaitCompletion());
+ assertEquals(-1, handler.result);
+ assertSame(attachment, handler.attachment);
+
+ handler = new RecordingHandler();
+ attachment = "ATTACHMENT4";
+ afc.read(buf, 9, attachment, handler);
+ assertTrue(handler.awaitCompletion());
+ assertEquals(-1, handler.result);
+ assertSame(attachment, handler.attachment);
+
+ handler = new RecordingHandler();
+ attachment = "ATTACHMENT5";
+ try {
+ afc.read(buf, -1, attachment, handler);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ afc.read(null, 1, attachment, handler);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ afc.read(buf.asReadOnlyBuffer(), 1, attachment, handler);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ afc.close();
+ }
+
+ @Test
+ public void testWrite_Future() throws Throwable {
+ byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+ File temp = createTemporaryFile(contents);
+
+ byte[] readBuf = new byte[4];
+ ByteBuffer buf = ByteBuffer.wrap(readBuf);
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.READ);
+ try {
+ afc.write(buf, 0);
+ fail();
+ } catch (NonWritableChannelException exception) {
+ }
+
+ afc.close();
+
+ afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.WRITE, StandardOpenOption.READ);
+
+ assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y'}), 0).get());
+
+ Future<Integer> fut = afc.read(buf, 0);
+
+ assertEquals(4, (int) fut.get());
+ buf.flip();
+ assertEquals('x', readBuf[0]);
+ assertEquals('y', readBuf[1]);
+ assertEquals('c', readBuf[2]);
+ assertEquals('d', readBuf[3]);
+
+ // Write that expands beyond the end of the file.
+ assertEquals(3, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y', 'z'}), 6).get());
+ assertEquals(9, afc.size());
+
+ buf.rewind();
+ fut = afc.read(buf, 6);
+ assertEquals(3, (int) fut.get());
+ buf.flip();
+ assertEquals('x', readBuf[0]);
+ assertEquals('y', readBuf[1]);
+ assertEquals('z', readBuf[2]);
+
+ // Writes at the end of the file.
+ assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y' }), 9).get());
+ assertEquals(11, afc.size());
+ buf.rewind();
+ fut = afc.read(buf, 9);
+ assertEquals(2, (int) fut.get());
+ buf.flip();
+ assertEquals('x', readBuf[0]);
+ assertEquals('y', readBuf[1]);
+
+ // Writes past the end of the file.
+ assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { '0', '2' }), 13).get());
+ assertEquals(15, afc.size());
+
+ // This is broken behaviour. At this point, there are 4 readable bytes in the file
+ // and our buffer should be filled with {0, 0, 48, 50}...
+ buf.rewind();
+ fut = afc.read(buf, 11);
+ assertEquals(2, (int) fut.get());
+ buf.flip();
+ assertEquals(0, readBuf[0]);
+ assertEquals(0, readBuf[1]);
+
+ // ... if we explicitly read at position 13, things are somehow fine again.
+ buf.rewind();
+ fut = afc.read(buf, 13);
+ assertEquals(2, (int) fut.get());
+ buf.flip();
+ assertEquals('0', readBuf[0]);
+ assertEquals('2', readBuf[1]);
+
+ try {
+ afc.write(buf, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ afc.write(null, 1);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ afc.close();
+ }
+
+ @Test
+ public void testWrite_CompletionListener() throws Throwable {
+ byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+ File temp = createTemporaryFile(contents);
+
+ byte[] readBuf = new byte[4];
+ ByteBuffer buf = ByteBuffer.wrap(readBuf);
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.READ);
+
+ String attachment = "ATTACHMENT";
+ RecordingHandler handler = new RecordingHandler();
+ try {
+ afc.write(buf, 0, attachment, handler);
+ fail();
+ } catch (NonWritableChannelException exception) {
+ }
+
+ afc.close();
+
+ afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.WRITE, StandardOpenOption.READ);
+
+ attachment = "ATTACHMENT";
+ handler = new RecordingHandler();
+ afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y'}), 0, attachment, handler);
+ assertTrue(handler.awaitCompletion());
+ assertSame(attachment, handler.attachment);
+ assertEquals(2, handler.result);
+
+ Future<Integer> fut = afc.read(buf, 0);
+
+ assertEquals(4, (int) fut.get());
+ buf.flip();
+ assertEquals('x', readBuf[0]);
+ assertEquals('y', readBuf[1]);
+ assertEquals('c', readBuf[2]);
+ assertEquals('d', readBuf[3]);
+
+ // Write that expands beyond the end of the file.
+ attachment = "ATTACHMENT2";
+ handler = new RecordingHandler();
+ afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y', 'z'}), 6, attachment, handler);
+ assertTrue(handler.awaitCompletion());
+ assertSame(attachment, handler.attachment);
+ assertEquals(3, handler.result);
+
+ assertEquals(9, afc.size());
+
+ buf.rewind();
+ fut = afc.read(buf, 6);
+ assertEquals(3, (int) fut.get());
+ buf.flip();
+ assertEquals('x', readBuf[0]);
+ assertEquals('y', readBuf[1]);
+ assertEquals('z', readBuf[2]);
+
+ // Writes at the end of the file.
+ attachment = "ATTACHMENT3";
+ handler = new RecordingHandler();
+ afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y' }), 9, attachment, handler);
+ assertTrue(handler.awaitCompletion());
+ assertSame(attachment, handler.attachment);
+ assertEquals(2, handler.result);
+
+ assertEquals(11, afc.size());
+ buf.rewind();
+ fut = afc.read(buf, 9);
+ assertEquals(2, (int) fut.get());
+ buf.flip();
+ assertEquals('x', readBuf[0]);
+ assertEquals('y', readBuf[1]);
+
+ // Writes past the end of the file.
+ attachment = "ATTACHMENT4";
+ handler = new RecordingHandler();
+ afc.write(ByteBuffer.wrap(new byte[] { '0', '2' }), 13, attachment, handler);
+ assertTrue(handler.awaitCompletion());
+ assertSame(attachment, handler.attachment);
+ assertEquals(2, handler.result);
+
+ assertEquals(15, afc.size());
+
+ // This is broken behaviour. At this point, there are 4 readable bytes in the file
+ // and our buffer should be filled with {0, 0, 48, 50}...
+ buf.rewind();
+ fut = afc.read(buf, 11);
+ assertEquals(2, (int) fut.get());
+ buf.flip();
+ assertEquals(0, readBuf[0]);
+ assertEquals(0, readBuf[1]);
+
+ // ... if we explicitly read at position 13, things are somehow fine again.
+ buf.rewind();
+ fut = afc.read(buf, 13);
+ assertEquals(2, (int) fut.get());
+ buf.flip();
+ assertEquals('0', readBuf[0]);
+ assertEquals('2', readBuf[1]);
+
+ try {
+ afc.write(buf, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ afc.write(null, 1);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ afc.close();
+ }
+
+ @Test
+ public void testWrite_Append() throws Throwable {
+ File temp = createTemporaryFile(256);
+
+ try {
+ AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.WRITE, StandardOpenOption.APPEND);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testSize() throws Throwable {
+ File temp = createTemporaryFile(256);
+ assertEquals(256, temp.length());
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.WRITE);
+ // Test initial size.
+ assertEquals(256, afc.size());
+
+ // Test that the size is updated after a write at the end of the file.
+ ByteBuffer buf = ByteBuffer.allocate(16);
+ assertEquals(16, (int) afc.write(buf, 256).get());
+ assertEquals(272, afc.size());
+
+ // Test that the size is updated after a truncate.
+ afc.truncate(16);
+ assertEquals(16, afc.size());
+ afc.close();
+ }
+
+ @Test
+ public void testTruncate() throws Throwable {
+ File temp = createTemporaryFile(256);
+ assertEquals(256, temp.length());
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+ StandardOpenOption.WRITE);
+ afc.truncate(128);
+ assertEquals(128, afc.size());
+ assertEquals(128, temp.length());
+
+ afc.truncate(0);
+ assertEquals(0, afc.size());
+ assertEquals(0, temp.length());
+
+ // Should be a no-op if the length is greater than the current length.
+ afc.truncate(128);
+ assertEquals(0, afc.size());
+ assertEquals(0, temp.length());
+
+ try {
+ afc.truncate(-1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ afc.close();
+
+ // Attempts to truncate a file that's not writeable should throw a NWCE.
+ temp = createTemporaryFile(256);
+ afc = AsynchronousFileChannel.open(temp.toPath(), StandardOpenOption.READ);
+ try {
+ afc.truncate(128);
+ fail();
+ } catch (NonWritableChannelException expected) {
+ }
+
+ try {
+ afc.truncate(384);
+ fail();
+ } catch (NonWritableChannelException expected) {
+ }
+
+ afc.close();
+ }
+
+ @Test
+ public void testCustomExecutor() throws Throwable {
+ AtomicReference<Thread> serviceThread = new AtomicReference<>();
+ ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ serviceThread.set(new Thread(r));
+ return serviceThread.get();
+ }
+ });
+
+ Set<OpenOption> openOptions = new HashSet<>();
+ openOptions.add(StandardOpenOption.READ);
+ openOptions.add(StandardOpenOption.WRITE);
+
+ AsynchronousFileChannel afc = AsynchronousFileChannel.open(
+ Files.createTempFile("AFCTest_testCustomExecutor", ""),
+ openOptions, executorService);
+
+ final LinkedBlockingQueue<Thread> observedThreads = new LinkedBlockingQueue<>();
+ CompletionHandler<Integer, String> handler = new CompletionHandler<Integer, String>() {
+ @Override
+ public void completed(Integer result, String attachment) {
+ assertTrue(observedThreads.offer(Thread.currentThread()));
+ }
+
+ @Override
+ public void failed(Throwable exc, String attachment) {
+ assertTrue(observedThreads.offer(Thread.currentThread()));
+ }
+ };
+
+ afc.write(ByteBuffer.allocate(16), 0, "foo", handler);
+ assertSame(serviceThread.get(), observedThreads.take());
+ assertEquals(0, observedThreads.size());
+
+ afc.read(ByteBuffer.allocate(16), 0, "foo", handler);
+ assertSame(serviceThread.get(), observedThreads.take());
+ assertEquals(0, observedThreads.size());
+ }
+}
diff --git a/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java b/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java
index 4bbf3a9..c867250 100644
--- a/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java
@@ -17,12 +17,25 @@
package libcore.java.nio.channels;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousByteChannel;
import java.nio.channels.Channels;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.Pipe;
import java.nio.channels.WritableByteChannel;
+import java.util.Arrays;
+import java.util.concurrent.Future;
import junit.framework.TestCase;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
public final class ChannelsTest extends TestCase {
@@ -56,5 +69,60 @@
sourceChannel.configureBlocking(false);
return sourceChannel;
}
-}
+ public void testInputStreamAsynchronousByteChannel() throws Exception {
+ AsynchronousByteChannel abc = mock(AsynchronousByteChannel.class);
+ InputStream is = Channels.newInputStream(abc);
+ Future<Integer> result = mock(Future.class);
+ ArgumentCaptor<ByteBuffer> bbCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
+ final byte[] bytesRead = new byte[10];
+
+ when(abc.read(bbCaptor.capture())).thenReturn(result);
+ when(result.get()).thenAnswer(
+ new Answer<Integer>() {
+ public Integer answer(InvocationOnMock invocation) {
+ ByteBuffer bb = bbCaptor.getValue();
+ assertEquals(bytesRead.length, bb.remaining());
+ // Write '7' bytes
+ bb.put(new byte[] {0, 1, 2, 3, 4, 5, 6});
+ return 7;
+ }
+ });
+
+ assertEquals(7, is.read(bytesRead));
+ // Only 7 bytes of data should be written into the buffer
+ byte[] bytesExpected = new byte[] { 0, 1, 2, 3, 4, 5, 6, 0, 0, 0 };
+ assertTrue(Arrays.equals(bytesExpected, bytesRead));
+
+ Mockito.verify(abc).read(isA(ByteBuffer.class));
+ Mockito.verify(result).get();
+ }
+
+ public void testOutputStreamAsynchronousByteChannel() throws Exception {
+ AsynchronousByteChannel abc = mock(AsynchronousByteChannel.class);
+ OutputStream os = Channels.newOutputStream(abc);
+ Future<Integer> result = mock(Future.class);
+ ArgumentCaptor<ByteBuffer> bbCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
+ final byte[] data = "world".getBytes();
+
+ when(abc.write(bbCaptor.capture())).thenReturn(result);
+ when(result.get()).thenAnswer(
+ new Answer<Integer>() {
+ public Integer answer(InvocationOnMock invocation) {
+ ByteBuffer bb = bbCaptor.getValue();
+ assertEquals(data.length, bb.remaining());
+ byte[] readData = new byte[data.length];
+ // Read the whole thing
+ bb.get(readData);
+ assertTrue(Arrays.equals(data, readData));
+ return data.length;
+ }
+ });
+
+ os.write(data);
+
+ Mockito.verify(abc).write(isA(ByteBuffer.class));
+ Mockito.verify(result).get();
+ }
+
+}
diff --git a/luni/src/test/java/libcore/java/nio/file/Files2Test.java b/luni/src/test/java/libcore/java/nio/file/Files2Test.java
index 44c4ec9..e23ae6a 100644
--- a/luni/src/test/java/libcore/java/nio/file/Files2Test.java
+++ b/luni/src/test/java/libcore/java/nio/file/Files2Test.java
@@ -23,7 +23,19 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.NonReadableChannelException;
+import java.nio.channels.NonWritableChannelException;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.MalformedInputException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.AccessDeniedException;
import java.nio.file.CopyOption;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
@@ -34,6 +46,8 @@
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
+import java.nio.file.NotDirectoryException;
+import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
@@ -44,22 +58,40 @@
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.spi.FileSystemProvider;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import sun.security.action.GetPropertyAction;
import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.FileVisitResult.TERMINATE;
+import static java.nio.file.StandardOpenOption.APPEND;
+import static java.nio.file.StandardOpenOption.CREATE_NEW;
+import static java.nio.file.StandardOpenOption.READ;
+import static java.nio.file.StandardOpenOption.SYNC;
+import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
+import static java.nio.file.StandardOpenOption.WRITE;
+import static java.security.AccessController.doPrivileged;
import static junit.framework.TestCase.assertTrue;
import static libcore.java.nio.file.FilesSetup.DATA_FILE;
import static libcore.java.nio.file.FilesSetup.NON_EXISTENT_FILE;
+import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA;
+import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA_2;
+import static libcore.java.nio.file.FilesSetup.UTF_16_DATA;
import static libcore.java.nio.file.FilesSetup.execCmdAndWaitForTermination;
+import static libcore.java.nio.file.FilesSetup.readFromFile;
import static libcore.java.nio.file.FilesSetup.readFromInputStream;
+import static libcore.java.nio.file.FilesSetup.writeToFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -243,11 +275,6 @@
}
@Test
- public void test_setOwner() throws IOException {
- // TODO: unable to set the owner due to insufficient permissions.
- }
-
- @Test
public void test_isSymbolicLink() throws IOException, InterruptedException {
assertFalse(Files.isSymbolicLink(filesSetup.getTestPath()));
assertFalse(Files.isSymbolicLink(filesSetup.getDataFilePath()));
@@ -794,6 +821,987 @@
} catch (FileSystemLoopException expected) {}
}
+ @Test
+ public void test_find() throws IOException {
+ // Directory structure.
+ // root
+ // ├── dir1
+ // │ ├── dir2
+ // │ │ ├── dir3
+ // │ │ └── file5
+ // │ ├── dir4
+ // │ └── file3
+ // ├── dir5
+ // └── file1
+ //
+
+ // Directory setup.
+ Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+ Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+ Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2");
+ Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3");
+ Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4");
+ Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5");
+ Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1");
+ Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3");
+ Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5");
+
+ Files.createDirectories(dir3);
+ Files.createDirectories(dir4);
+ Files.createDirectories(dir5);
+ Files.createFile(file1);
+ Files.createFile(file3);
+ Files.createFile(file5);
+
+ // When depth is 2 then file4, file5 and dir3 are not reachable.
+ Set<Path> expectedDirSet = new HashSet<>();
+ expectedDirSet.add(rootDir);
+ expectedDirSet.add(dir1);
+ expectedDirSet.add(dir2);
+ expectedDirSet.add(dir4);
+ expectedDirSet.add(dir5);
+ Set<Path> dirSet = new HashSet<>();
+ Stream<Path> pathStream = Files.find(rootDir, 2, (path, attr) -> Files.isDirectory(path));
+ pathStream.forEach(path -> dirSet.add(path));
+ assertEquals(expectedDirSet, dirSet);
+
+ // Test the case where depth is 0.
+ expectedDirSet.clear();
+ dirSet.clear();
+
+ expectedDirSet.add(rootDir);
+
+ pathStream = Files.find(rootDir, 0, (path, attr) -> Files.isDirectory(path));
+ pathStream.forEach(path -> dirSet.add(path));
+ assertEquals(expectedDirSet, dirSet);
+
+ // Test the case where depth is -1.
+ try {
+ Files.find(rootDir, -1, (path, attr) -> Files.isDirectory(path));
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ // Test the case when BiPredicate always returns false.
+ expectedDirSet.clear();
+ dirSet.clear();
+
+ pathStream = Files.find(rootDir, 2, (path, attr) -> false);
+ pathStream.forEach(path -> dirSet.add(path));
+ assertEquals(expectedDirSet, dirSet);
+
+ // Test the case when start is not a directory.
+ expectedDirSet.clear();
+ dirSet.clear();
+
+ expectedDirSet.add(file1);
+
+ pathStream = Files.find(file1, 2, (path, attr) -> true);
+ pathStream.forEach(path -> dirSet.add(path));
+ assertEquals(expectedDirSet, dirSet);
+ }
+
+ @Test
+ public void test_find_NPE() throws IOException {
+ Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+ Files.createDirectories(rootDir);
+ try {
+ Files.find(null, 2, (path, attr) -> Files.isDirectory(path));
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ Files.find(rootDir, (Integer)null, (path, attr) -> Files.isDirectory(path));
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try(Stream<Path> pathStream = Files.find(rootDir, 2, null)) {
+ pathStream.forEach(path -> {/* do nothing */});
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_lines$Path$Charset() throws IOException {
+ List<String> lines = new ArrayList<>();
+ lines.add(UTF_16_DATA);
+ lines.add(TEST_FILE_DATA);
+ Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_16);
+ try (Stream<String> readLines = Files.lines(filesSetup.getDataFilePath(),
+ StandardCharsets.UTF_16)) {
+ Iterator<String> lineIterator = lines.iterator();
+ readLines.forEach(line -> assertEquals(line, lineIterator.next()));
+ }
+
+ // When Path is a directory
+ filesSetup.reset();
+ try (Stream<String> readLines = Files.lines(filesSetup.getTestDirPath(),
+ StandardCharsets.UTF_16)) {
+ try {
+ readLines.count();
+ fail();
+ } catch (UncheckedIOException expected) {}
+ }
+
+ // When file doesn't exits.
+ filesSetup.reset();
+ try (Stream<String> readLines = Files.lines(filesSetup.getTestPath(),
+ StandardCharsets.UTF_16)) {
+ fail();
+ } catch (NoSuchFileException expected) {}
+ }
+
+ @Test
+ public void test_lines$Path$Charset_NPE() throws IOException {
+ try {
+ Files.lines(null, StandardCharsets.UTF_16);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.lines(filesSetup.getDataFilePath(), null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_lines$Path() throws IOException {
+ List<String> lines = new ArrayList<>();
+ lines.add(TEST_FILE_DATA_2);
+ lines.add(TEST_FILE_DATA);
+ Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_8);
+ try (Stream<String> readLines = Files.lines(filesSetup.getDataFilePath())) {
+ Iterator<String> lineIterator = lines.iterator();
+ readLines.forEach(line -> assertEquals(line, lineIterator.next()));
+ }
+
+ // When Path is a directory
+ filesSetup.reset();
+ try (Stream<String> readLines = Files.lines(filesSetup.getTestDirPath())) {
+ try {
+ readLines.count();
+ fail();
+ } catch (UncheckedIOException expected) {}
+ }
+
+ // When file doesn't exits.
+ filesSetup.reset();
+ try (Stream<String> readLines = Files.lines(filesSetup.getTestPath())) {
+ fail();
+ } catch (NoSuchFileException expected) {}
+ }
+
+ @Test
+ public void test_line$Path_NPE() throws IOException {
+ try {
+ Files.lines(null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_list() throws Exception {
+ // Directory Setup for the test.
+ Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+ Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+ Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1");
+ Path file2 = Paths.get(filesSetup.getTestDir(), "root/dir1/file2");
+ Path symLink = Paths.get(filesSetup.getTestDir(), "root/symlink");
+ Files.createDirectories(dir1);
+ Files.createFile(file1);
+ Files.createFile(file2);
+ Files.createSymbolicLink(symLink, file1.toAbsolutePath());
+
+ Set<Path> expectedVisitedFiles = new HashSet<>();
+ expectedVisitedFiles.add(dir1);
+ expectedVisitedFiles.add(file1);
+ expectedVisitedFiles.add(symLink);
+
+ Set<Path> visitedFiles = new HashSet<>();
+ try (Stream<Path> pathStream = Files.list(rootDir)) {
+ pathStream.forEach(path -> visitedFiles.add(path));
+ }
+ assertEquals(3, visitedFiles.size());
+
+
+ // Test the case where directory is empty.
+ filesSetup.clearAll();
+ try {
+ Files.list(Paths.get(filesSetup.getTestDir(), "newDir"));
+ fail();
+ } catch (NoSuchFileException expected) {}
+
+ // Test the case where path points to a file.
+ filesSetup.clearAll();
+ filesSetup.setUp();
+ try {
+ Files.list(filesSetup.getDataFilePath());
+ fail();
+ } catch (NotDirectoryException expected) {}
+ }
+
+ @Test
+ public void test_list_NPE() throws IOException {
+ try {
+ Files.list(null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_newBufferedReader() throws IOException {
+ // Test the case where file doesn't exists.
+ try {
+ Files.newBufferedReader(filesSetup.getTestPath());
+ fail();
+ } catch (NoSuchFileException expected) {}
+
+ BufferedReader bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath());
+ assertEquals(TEST_FILE_DATA, bufferedReader.readLine());
+
+ // Test the case where the file content has unicode characters.
+ writeToFile(filesSetup.getDataFilePath(), UTF_16_DATA);
+ bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath());
+ assertEquals(UTF_16_DATA, bufferedReader.readLine());
+ bufferedReader.close();
+
+ // Test the case where file is write-only.
+ Set<PosixFilePermission> perm = PosixFilePermissions.fromString("-w-------");
+ Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm);
+ try {
+ Files.newBufferedReader(filesSetup.getDataFilePath());
+ fail();
+ } catch (AccessDeniedException expected) {}
+ }
+
+ @Test
+ public void test_newBufferedReader_NPE() throws IOException {
+ try {
+ Files.newBufferedReader(null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_newBufferedReader$Path$Charset() throws IOException {
+ BufferedReader bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath(),
+ StandardCharsets.US_ASCII);
+ assertEquals(TEST_FILE_DATA, bufferedReader.readLine());
+
+ // When the file has unicode characters.
+ writeToFile(filesSetup.getDataFilePath(), UTF_16_DATA);
+ bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath(),
+ StandardCharsets.US_ASCII);
+ try {
+ bufferedReader.readLine();
+ fail();
+ } catch (MalformedInputException expected) {}
+ }
+
+ @Test
+ public void test_newBufferedReader$Path$Charset_NPE() throws IOException {
+ try {
+ Files.newBufferedReader(null, StandardCharsets.US_ASCII);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.newBufferedReader(filesSetup.getDataFilePath(), null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_newBufferedWriter() throws IOException {
+ BufferedWriter bufferedWriter = Files.newBufferedWriter(filesSetup.getTestPath());
+ bufferedWriter.write(TEST_FILE_DATA);
+ bufferedWriter.close();
+ assertEquals(TEST_FILE_DATA,
+ readFromFile(filesSetup.getTestPath()));
+
+ // When file exists, it should start writing from the beginning.
+ bufferedWriter = Files.newBufferedWriter(filesSetup.getDataFilePath());
+ bufferedWriter.write(TEST_FILE_DATA_2);
+ bufferedWriter.close();
+ assertEquals(TEST_FILE_DATA_2,
+ readFromFile(filesSetup.getDataFilePath()));
+
+ // When file is read-only.
+ Set<PosixFilePermission> perm = PosixFilePermissions.fromString("r--------");
+ FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+ Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm);
+ try {
+ Files.newBufferedWriter(filesSetup.getDataFilePath());
+ fail();
+ } catch (AccessDeniedException expected) {}
+ }
+
+ @Test
+ public void test_newBufferedWriter_NPE() throws IOException {
+ try {
+ Files.newBufferedWriter(null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_newBufferedWriter$Path$Charset() throws IOException {
+ BufferedWriter bufferedWriter = Files.newBufferedWriter(filesSetup.getTestPath(),
+ StandardCharsets.US_ASCII);
+ bufferedWriter.write(TEST_FILE_DATA);
+ bufferedWriter.close();
+ assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath()));
+ }
+
+ @Test
+ public void test_newBufferedWriter$Path$Charset_NPE() throws IOException {
+ try {
+ Files.newBufferedWriter(null, StandardCharsets.US_ASCII);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.newBufferedWriter(filesSetup.getTestPath(), (OpenOption[]) null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_newByteChannel() throws IOException {
+ // When file doesn't exist
+ try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getTestPath())) {
+ fail();
+ } catch (NoSuchFileException expected) {
+ }
+
+ // When file exists.
+
+ // File opens in READ mode by default. The channel is non writable by default.
+ try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath())) {
+ sbc.write(ByteBuffer.allocate(10));
+ fail();
+ } catch (NonWritableChannelException expected) {
+ }
+
+ // Read a file.
+ try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath())) {
+ ByteBuffer readBuffer = ByteBuffer.allocate(10);
+ int bytesReadCount = sbc.read(readBuffer);
+
+ String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount),
+ StandardCharsets.UTF_8);
+ assertEquals(TEST_FILE_DATA, readData);
+ }
+ }
+
+ @Test
+ public void test_newByteChannel_openOption_WRITE() throws IOException {
+ // When file doesn't exist
+ try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getTestPath(), WRITE)) {
+ fail();
+ } catch (NoSuchFileException expected) {
+ }
+
+ // When file exists.
+
+ try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE)) {
+ sbc.read(ByteBuffer.allocate(10));
+ fail();
+ } catch (NonReadableChannelException expected) {
+ }
+
+ // Write in file.
+ try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE)) {
+ sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes()));
+ sbc.close();
+
+ try (InputStream is = Files.newInputStream(filesSetup.getDataFilePath())) {
+ String expectedFileData = TEST_FILE_DATA_2 +
+ TEST_FILE_DATA.substring(
+ TEST_FILE_DATA_2.length());
+ assertEquals(expectedFileData, readFromInputStream(is));
+ }
+ }
+ }
+
+ @Test
+ public void test_newByteChannel_openOption_WRITE_READ() throws IOException {
+ try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE,
+ READ, SYNC/* Sync makes sure the that InputStream is able to read content written by
+ the seekable byte channel without closing/flushing it. */)) {
+ ByteBuffer readBuffer = ByteBuffer.allocate(10);
+ int bytesReadCount = sbc.read(readBuffer);
+
+ String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount),
+ StandardCharsets.UTF_8);
+ assertEquals(TEST_FILE_DATA, readData);
+
+ // Pointer will move to the end of the file after read operation. The write should
+ // append the data at the end of the file.
+ sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes()));
+ try (InputStream is = Files.newInputStream(filesSetup.getDataFilePath())) {
+ String expectedFileData = TEST_FILE_DATA + TEST_FILE_DATA_2;
+ assertEquals(expectedFileData, readFromInputStream(is));
+ }
+ }
+ }
+
+ @Test
+ public void test_newByteChannel_NPE() throws IOException {
+ try (SeekableByteChannel sbc = Files.newByteChannel(null)) {
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(),
+ (OpenOption[]) null)) {
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_readAllLine() throws IOException {
+ // Multi-line file.
+ assertTrue(Files.exists(filesSetup.getDataFilePath()));
+ writeToFile(filesSetup.getDataFilePath(), "\n" + TEST_FILE_DATA_2,
+ APPEND);
+ List<String> out = Files.readAllLines(filesSetup.getDataFilePath());
+ assertEquals(2, out.size());
+ assertEquals(TEST_FILE_DATA, out.get(0));
+ assertEquals(TEST_FILE_DATA_2, out.get(1));
+
+ // When file doesn't exist.
+ filesSetup.reset();
+ try {
+ Files.readAllLines(filesSetup.getTestPath());
+ fail();
+ } catch (NoSuchFileException expected) {}
+
+ // When file is a directory.
+ filesSetup.reset();
+ try {
+ Files.readAllLines(filesSetup.getTestDirPath());
+ fail();
+ } catch (IOException expected) {}
+ }
+
+ @Test
+ public void test_readAllLine_NPE() throws IOException {
+ try {
+ Files.readAllLines(null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_readAllLine$Path$Charset() throws IOException {
+ assertTrue(Files.exists(filesSetup.getDataFilePath()));
+ writeToFile(filesSetup.getDataFilePath(), "\n" + TEST_FILE_DATA_2, APPEND);
+ List<String> out = Files.readAllLines(filesSetup.getDataFilePath(), StandardCharsets.UTF_8);
+ assertEquals(2, out.size());
+ assertEquals(TEST_FILE_DATA, out.get(0));
+ assertEquals(TEST_FILE_DATA_2, out.get(1));
+
+ // With UTF-16.
+ out = Files.readAllLines(filesSetup.getDataFilePath(), StandardCharsets.UTF_16);
+ assertEquals(1, out.size());
+
+ // UTF-8 data read as UTF-16
+ String expectedOutput = new String((TEST_FILE_DATA + '\n' + TEST_FILE_DATA_2).getBytes(),
+ StandardCharsets.UTF_16);
+ assertEquals(expectedOutput, out.get(0));
+
+ // When file doesn't exist.
+ filesSetup.reset();
+ try {
+ Files.readAllLines(filesSetup.getTestPath(), StandardCharsets.UTF_16);
+ fail();
+ } catch (NoSuchFileException expected) {}
+
+ // When file is a directory.
+ filesSetup.reset();
+ try {
+ Files.readAllLines(filesSetup.getTestDirPath(), StandardCharsets.UTF_16);
+ fail();
+ } catch (IOException expected) {}
+ }
+
+ @Test
+ public void test_readAllLine$Path$Charset_NPE() throws IOException {
+ try {
+ Files.readAllLines(null, StandardCharsets.UTF_16);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.readAllLines(filesSetup.getDataFilePath(), null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_walk$Path$FileVisitOption() throws IOException {
+ // Directory structure.
+ // root
+ // ├── dir1
+ // │ ├── dir2
+ // │ │ ├── dir3
+ // │ │ └── file5
+ // │ ├── dir4
+ // │ └── file3
+ // ├── dir5
+ // └── file1
+ //
+ // depth will be 2. file4, file5, dir3 is not reachable.
+
+ Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+ Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+ Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2");
+ Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3");
+ Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4");
+ Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5");
+ Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1");
+ Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3");
+ Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5");
+
+ Files.createDirectories(dir3);
+ Files.createDirectories(dir4);
+ Files.createDirectories(dir5);
+ Files.createFile(file1);
+ Files.createFile(file3);
+ Files.createFile(file5);
+
+ Set<Path> expectedDirSet = new HashSet<>();
+ expectedDirSet.add(rootDir);
+ expectedDirSet.add(dir1);
+ expectedDirSet.add(dir2);
+ expectedDirSet.add(dir4);
+ expectedDirSet.add(file3);
+ expectedDirSet.add(dir5);
+ expectedDirSet.add(file1);
+
+ Set<Path> dirSet = new HashSet<>();
+ try(Stream<Path> pathStream = Files.walk(rootDir, 2,
+ FileVisitOption.FOLLOW_LINKS)) {
+ pathStream.forEach(path -> dirSet.add(path));
+ }
+
+ assertEquals(expectedDirSet, dirSet);
+
+ // Test case when Path doesn't exist.
+ try (Stream<Path> pathStream = Files.walk(filesSetup.getTestPath(), 2,
+ FileVisitOption.FOLLOW_LINKS)){
+ fail();
+ } catch (NoSuchFileException expected) {}
+
+ // Test case when Path is a not a directory.
+ expectedDirSet.clear();
+ dirSet.clear();
+ expectedDirSet.add(filesSetup.getDataFilePath());
+ try (Stream<Path> pathStream = Files.walk(filesSetup.getDataFilePath(), 2,
+ FileVisitOption.FOLLOW_LINKS)){
+ pathStream.forEach(path -> dirSet.add(path));
+ }
+ assertEquals(expectedDirSet, dirSet);
+
+ // Test case when Path doesn't exist.
+ try (Stream<Path> pathStream = Files.walk(rootDir, -1, FileVisitOption.FOLLOW_LINKS)){
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ @Test
+ public void test_walk_FileSystemLoopException() throws IOException {
+ // Directory structure.
+ // root
+ // └── dir1
+ // └── file1
+ //
+ // file1 is symbolic link to dir1
+
+ Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+ Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir");
+ Path file1 = Paths.get(filesSetup.getTestDir(), "root/dir/file1");
+ Files.createDirectories(dir1);
+ Files.createSymbolicLink(file1, dir1.toAbsolutePath());
+ assertTrue(Files.isSymbolicLink(file1));
+ try(Stream<Path> pathStream = Files.walk(rootDir, FileVisitOption.FOLLOW_LINKS)) {
+ pathStream.forEach(path -> assertNotNull(path));
+ fail();
+ } catch (UncheckedIOException expected) {
+ assertTrue(expected.getCause() instanceof FileSystemLoopException);
+ }
+ }
+
+ @Test
+ public void test_walk() throws IOException {
+ // Directory structure.
+ // root
+ // ├── dir1
+ // │ ├── dir2
+ // │ │ ├── dir3
+ // │ │ └── file5
+ // │ ├── dir4
+ // │ └── file3
+ // ├── dir5
+ // └── file1
+ //
+
+ Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+ Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+ Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2");
+ Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3");
+ Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4");
+ Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5");
+ Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1");
+ Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3");
+ Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5");
+
+ Files.createDirectories(dir3);
+ Files.createDirectories(dir4);
+ Files.createDirectories(dir5);
+ Files.createFile(file1);
+ Files.createFile(file3);
+ Files.createFile(file5);
+
+ Set<Path> expectedDirSet = new HashSet<>();
+ expectedDirSet.add(rootDir.getFileName());
+ expectedDirSet.add(dir1.getFileName());
+ expectedDirSet.add(dir2.getFileName());
+ expectedDirSet.add(dir4.getFileName());
+ expectedDirSet.add(file3.getFileName());
+ expectedDirSet.add(dir5.getFileName());
+ expectedDirSet.add(file1.getFileName());
+ expectedDirSet.add(file5.getFileName());
+ expectedDirSet.add(dir3.getFileName());
+
+ Set<Path> dirSet = new HashSet<>();
+ try (Stream<Path> pathStream = Files.walk(rootDir)) {
+ pathStream.forEach(path -> dirSet.add(path.getFileName()));
+ }
+
+ assertEquals(expectedDirSet, dirSet);
+
+
+ // Test case when Path doesn't exist.
+ try (Stream<Path> pathStream = Files.walk(filesSetup.getTestPath())){
+ fail();
+ } catch (NoSuchFileException expected) {}
+
+ // Test case when Path is a not a directory.
+ expectedDirSet.clear();
+ dirSet.clear();
+ expectedDirSet.add(filesSetup.getDataFilePath());
+ try (Stream<Path> pathStream = Files.walk(filesSetup.getDataFilePath())) {
+ pathStream.forEach(path -> dirSet.add(path));
+ }
+ assertEquals(expectedDirSet, dirSet);
+ }
+
+ @Test
+ public void test_walk_depthFirst() throws IOException {
+ // Directory structure.
+ // root
+ // ├── dir1
+ // │ └── file1
+ // └── dir2
+ // └── file2
+ //
+
+ Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+ Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+ Path file1 = Paths.get(filesSetup.getTestDir(), "root/dir1/file1");
+ Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir2");
+ Path file2 = Paths.get(filesSetup.getTestDir(), "root/dir2/file2");
+ Files.createDirectories(dir1);
+ Files.createDirectories(dir2);
+ Files.createFile(file1);
+ Files.createFile(file2);
+ List<Object> fileKeyList = new ArrayList<>();
+ try(Stream<Path> pathStream = Files.walk(rootDir, FileVisitOption.FOLLOW_LINKS)) {
+ pathStream.forEach(path -> fileKeyList.add(path.getFileName()));
+ }
+ assertEquals(rootDir.getFileName(), fileKeyList.get(0));
+ if (fileKeyList.get(1).equals(dir1.getFileName())) {
+ assertEquals(file1.getFileName(), fileKeyList.get(2));
+ assertEquals(dir2.getFileName(), fileKeyList.get(3));
+ assertEquals(file2.getFileName(), fileKeyList.get(4));
+ } else if (fileKeyList.get(1).equals(dir2.getFileName())) {
+ assertEquals(file2.getFileName(), fileKeyList.get(2));
+ assertEquals(dir1.getFileName(), fileKeyList.get(3));
+ assertEquals(file1.getFileName(), fileKeyList.get(4));
+ } else {
+ fail();
+ }
+ }
+
+ @Test
+ public void test_walk$Path$Int$LinkOption_IllegalArgumentException() throws IOException {
+ Map<Path, Boolean> dirMap = new HashMap<>();
+ Path rootDir = Paths.get(filesSetup.getTestDir(), "rootDir");
+ try (Stream<Path> pathStream = Files.walk(rootDir, -1,
+ FileVisitOption.FOLLOW_LINKS)) {
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ @Test
+ public void test_walk$Path$FileVisitOption_NPE() throws IOException {
+ try {
+ Files.walk(null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_write$Path$byte$OpenOption() throws IOException {
+ Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes());
+ assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getDataFilePath()));
+ }
+
+ @Test
+ public void test_write$Path$byte$OpenOption_OpenOption() throws IOException {
+ Files.write(filesSetup.getTestPath(), TEST_FILE_DATA_2.getBytes(), CREATE_NEW);
+ assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getTestPath()));
+
+ filesSetup.reset();
+ Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), TRUNCATE_EXISTING);
+ assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getDataFilePath()));
+
+ filesSetup.reset();
+ Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), APPEND);
+ assertEquals(TEST_FILE_DATA + TEST_FILE_DATA_2, readFromFile(
+ filesSetup.getDataFilePath()));
+
+ filesSetup.reset();
+ try {
+ Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), READ);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ @Test
+ public void test_write$Path$byte$OpenOption_NPE() throws IOException {
+ try {
+ Files.write(null, TEST_FILE_DATA_2.getBytes(), CREATE_NEW);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.write(filesSetup.getTestPath(), (byte[]) null, CREATE_NEW);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.write(filesSetup.getTestPath(), TEST_FILE_DATA_2.getBytes(), (OpenOption[]) null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_write$Path$Iterable$Charset$OpenOption() throws IOException {
+ List<String> lines = new ArrayList<>();
+ lines.add(TEST_FILE_DATA_2);
+ lines.add(TEST_FILE_DATA);
+ Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_16);
+ List<String> readLines = Files.readAllLines(filesSetup.getDataFilePath(),
+ StandardCharsets.UTF_16);
+ assertEquals(readLines, lines);
+ }
+
+ @Test
+ public void test_write$Path$Iterable$Charset$OpenOption_NPE() throws IOException {
+ try {
+ Files.write(null, new ArrayList<>(), StandardCharsets.UTF_16);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.write(filesSetup.getDataFilePath(), null, StandardCharsets.UTF_16);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.write(filesSetup.getDataFilePath(), new ArrayList<>(), (OpenOption[]) null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_write$Path$Iterable$OpenOption() throws IOException {
+ List<String> lines = new ArrayList<>();
+ lines.add(TEST_FILE_DATA_2);
+ lines.add(TEST_FILE_DATA);
+ Files.write(filesSetup.getDataFilePath(), lines);
+ List<String> readLines = Files.readAllLines(filesSetup.getDataFilePath());
+ assertEquals(readLines, lines);
+ }
+
+ @Test
+ public void test_write$Path$Iterable$OpenOption_NPE() throws IOException {
+ try {
+ Files.write(null, new ArrayList<String>());
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.write(filesSetup.getDataFilePath(), (Iterable<CharSequence>) null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ // The ability for Android apps to create hard links was removed in
+ // https://android-review.googlesource.com/144092 (March 2015).
+ // https://b/19953790.
+ @Test
+ public void test_createLink() throws IOException {
+ try {
+ Files.createLink(filesSetup.getTestPath(), filesSetup.getDataFilePath());
+ fail();
+ } catch (AccessDeniedException expected) {}
+ }
+
+ @Test
+ public void test_createTempDirectory$Path$String$FileAttributes() throws IOException {
+ Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
+ FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+
+ String tmpDir = "tmpDir";
+ Path tmpDirPath = Files.createTempDirectory(filesSetup.getTestDirPath(), tmpDir, attr);
+ assertTrue(tmpDirPath.getFileName().toString().startsWith(tmpDir));
+ assertEquals(filesSetup.getTestDirPath(), tmpDirPath.getParent());
+ assertTrue(Files.isDirectory(tmpDirPath));
+ assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name()));
+
+ filesSetup.reset();
+ // Test case when prefix is null.
+ tmpDirPath = Files.createTempDirectory(filesSetup.getTestDirPath(), null, attr);
+ assertEquals(filesSetup.getTestDirPath(), tmpDirPath.getParent());
+ assertTrue(Files.isDirectory(tmpDirPath));
+ assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name()));
+
+ try {
+ Files.createTempDirectory(null, tmpDir, attr);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.createTempDirectory(filesSetup.getTestDirPath(), tmpDir, null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_createTempDirectory$String$FileAttributes() throws IOException {
+ Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
+ FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+
+ Path tmpDirectoryLocation = Paths.get(System.getProperty("java.io.tmpdir"));
+
+ String tmpDir = "tmpDir";
+ Path tmpDirPath = Files.createTempDirectory(tmpDir, attr);
+ assertTrue(tmpDirPath.getFileName().toString().startsWith(tmpDir));
+ assertEquals(tmpDirectoryLocation, tmpDirPath.getParent());
+ assertTrue(Files.isDirectory(tmpDirPath));
+ assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name()));
+
+ // Test case when prefix is null.
+ filesSetup.reset();
+ tmpDirPath = Files.createTempDirectory(null, attr);
+ assertEquals(tmpDirectoryLocation, tmpDirPath.getParent());
+ assertTrue(Files.isDirectory(tmpDirPath));
+ assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name()));
+
+ try {
+ Files.createTempDirectory(tmpDir, null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_createTempFile$Path$String$String$FileAttributes() throws IOException {
+ Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
+ FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+
+ String tmpFilePrefix = "prefix";
+ String tmpFileSuffix = "suffix";
+
+ Path tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix,
+ tmpFileSuffix, attr);
+
+ assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix));
+ assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix));
+ assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent());
+ assertTrue(Files.isRegularFile(tmpFilePath));
+ assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+ // Test case when prefix is null.
+ filesSetup.reset();
+ tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), null,
+ tmpFileSuffix, attr);
+ assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix));
+ assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent());
+ assertTrue(Files.isRegularFile(tmpFilePath));
+ assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+ // Test case when suffix is null.
+ filesSetup.reset();
+ tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix,
+ null, attr);
+ assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix));
+ assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent());
+ assertTrue(Files.isRegularFile(tmpFilePath));
+ assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+ try {
+ Files.createTempFile(null, tmpFilePrefix, tmpFileSuffix, attr);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ try {
+ Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix, tmpFileSuffix,
+ null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void test_createTempFile$String$String$FileAttributes() throws IOException {
+ Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
+ FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+
+ Path tmpDirectoryLocation = Paths.get(System.getProperty(
+ "java.io.tmpdir"));
+
+ String tmpFilePrefix = "prefix";
+ String tmpFileSuffix = "suffix";
+ Path tmpFilePath = Files.createTempFile(tmpFilePrefix, tmpFileSuffix, attr);
+ assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix));
+ assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix));
+ assertEquals(tmpDirectoryLocation, tmpFilePath.getParent());
+ assertTrue(Files.isRegularFile(tmpFilePath));
+ assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+ // Test case when prefix is null.
+ filesSetup.reset();
+ tmpFilePath = Files.createTempFile(null, tmpFileSuffix, attr);
+ assertEquals(tmpDirectoryLocation, tmpFilePath.getParent());
+ assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix));
+ assertTrue(Files.isRegularFile(tmpFilePath));
+ assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+ // Test case when suffix is null.
+ filesSetup.reset();
+ tmpFilePath = Files.createTempFile(tmpFilePrefix, null, attr);
+ assertEquals(tmpDirectoryLocation, tmpFilePath.getParent());
+ assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix));
+ assertTrue(Files.isRegularFile(tmpFilePath));
+ assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+ try {
+ Files.createTempFile(tmpFilePrefix, tmpFileSuffix, null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
// -- Mock Class --
private static class TestFileVisitor implements FileVisitor<Path> {
diff --git a/luni/src/test/java/libcore/java/nio/file/FilesSetup.java b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
index 66201d9..70e717d 100644
--- a/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
+++ b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
@@ -45,6 +45,13 @@
final static String TEST_FILE_DATA_2 = "test";
+ /**
+ * Data that includes characters code above the US-ASCII range and will be more obviously
+ * corrupted if encoded / decoded incorrectly than
+ * {@link #TEST_FILE_DATA} / {@link #TEST_FILE_DATA_2}.
+ */
+ final static String UTF_16_DATA = "परीक्षण";
+
private String testDir;
private Path dataFilePath;
diff --git a/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java b/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java
index 63f3fd3..c53f531 100644
--- a/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java
@@ -113,17 +113,34 @@
// emit EVENT_CREATE
Files.createFile(file);
+ assertWatchServiceEvent(watchService, directoryKey1,
+ Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1)), true);
+ assertNull(watchService.poll());
+
// emit EVENT_MODIFY
Files.write(file, "hello1".getBytes());
+ assertWatchServiceEvent(watchService, directoryKey1,
+ Arrays.asList(new WatchEventResult(ENTRY_MODIFY)), true);
+
+ // http:///b/35346596
+ // Sometimes we receive a second, latent EVENT_MODIFY that happens shortly
+ // after the first one. This will intercept it and make sure it won't
+ // mess with ENTRY_DELETE later.
+ Thread.sleep(500);
+ WatchKey doubleModifyKey = watchService.poll();
+ if (doubleModifyKey != null) {
+ List<WatchEvent<?>> event = doubleModifyKey.pollEvents();
+ assertEquals(ENTRY_MODIFY, event.get(0).kind());
+ doubleModifyKey.reset();
+ }
+ assertNull(watchService.poll());
+
// emit EVENT_DELETE
Files.delete(file);
-
- // Don't assert count of ENTRY_MODIFY, it's very flaky, sometime it triggers
- // two events, sometime one....
assertWatchServiceEvent(watchService, directoryKey1,
- Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1),
- new WatchEventResult(ENTRY_MODIFY),
- new WatchEventResult(ENTRY_DELETE, 1)), true);
+ Arrays.asList(new WatchEventResult(ENTRY_DELETE, 1)), true);
+
+ // Assert no more events
assertNull(watchService.poll());
watchService.close();
}
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index d9d0d31..ffba98a 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -35,8 +35,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -44,6 +46,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -84,6 +87,20 @@
Set<Provider.Service> services = provider.getServices();
assertNotNull(services);
assertFalse(services.isEmpty());
+ if (LOG_DEBUG) {
+ Set<Provider.Service> originalServices = services;
+ services = new TreeSet<Provider.Service>(
+ new Comparator<Provider.Service>() {
+ public int compare(Provider.Service a, Provider.Service b) {
+ int typeCompare = a.getType().compareTo(b.getType());
+ if (typeCompare != 0) {
+ return typeCompare;
+ }
+ return a.getAlgorithm().compareTo(b.getAlgorithm());
+ }
+ });
+ services.addAll(originalServices);
+ }
for (Provider.Service service : services) {
String type = service.getType();
@@ -148,10 +165,28 @@
// assert that we don't have any extra in the implementation
Collections.sort(extra); // sort so that its grouped by type
- assertEquals("Extra algorithms", Collections.EMPTY_LIST, extra);
+ assertEquals("Algorithms are provided but not present in StandardNames",
+ Collections.EMPTY_LIST, extra);
+ if (remainingExpected.containsKey("Cipher")) {
+ // For any remaining ciphers, they may be aliases for other ciphers or otherwise
+ // don't show up as a service but can still be instantiated.
+ for (Iterator<String> cipherIt = remainingExpected.get("Cipher").iterator();
+ cipherIt.hasNext(); ) {
+ String missingCipher = cipherIt.next();
+ try {
+ Cipher.getInstance(missingCipher);
+ cipherIt.remove();
+ } catch (NoSuchAlgorithmException|NoSuchPaddingException e) {
+ }
+ }
+ if (remainingExpected.get("Cipher").isEmpty()) {
+ remainingExpected.remove("Cipher");
+ }
+ }
// assert that we don't have any missing in the implementation
- assertEquals("Missing algorithms", Collections.EMPTY_MAP, remainingExpected);
+ assertEquals("Algorithms are present in StandardNames but not provided",
+ Collections.EMPTY_MAP, remainingExpected);
// assert that we don't have any missing classes
Collections.sort(missing); // sort it for readability
diff --git a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
index b95f9fe..20fa986 100644
--- a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
@@ -557,4 +557,30 @@
dateFormat.setTimeZone(UTC);
assertEquals("UTC", dateFormat.format(new Date(0)));
}
+
+ // http://b/35134326
+ public void testTimeZoneParsingErrorIndex() {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy z", Locale.ENGLISH);
+
+ checkTimeZoneParsingErrorIndex(dateFormat);
+ }
+
+ // http://b/35134326
+ public void testTimeZoneParsingErrorIndexWithZoneStrings() {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy z", Locale.ENGLISH);
+ // Force legacy code path by using zone strings.
+ DateFormatSymbols dfs = dateFormat.getDateFormatSymbols();
+ dfs.setZoneStrings(dfs.getZoneStrings());
+ dateFormat.setDateFormatSymbols(dfs);
+
+ checkTimeZoneParsingErrorIndex(dateFormat);
+ }
+
+ private void checkTimeZoneParsingErrorIndex(SimpleDateFormat dateFormat) {
+ ParsePosition pos = new ParsePosition(0);
+ Date parsed;
+ parsed = dateFormat.parse("2000 foobar", pos);
+ assertNull(parsed);
+ assertEquals("Wrong error index", 5, pos.getErrorIndex());
+ }
}
diff --git a/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java b/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java
new file mode 100644
index 0000000..3b7255f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for {@link DateTimeException}.
+ */
+public class DateTimeExceptionTest {
+
+ @Test
+ public void test_constructor_message() {
+ DateTimeException ex = new DateTimeException("message");
+ assertEquals("message", ex.getMessage());
+ assertNull(ex.getCause());
+
+ }
+
+ @Test
+ public void test_constructor_message_cause() {
+ Throwable cause = new Exception();
+ DateTimeException ex = new DateTimeException("message", cause);
+ assertEquals("message", ex.getMessage());
+ assertSame(cause, ex.getCause());
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/DurationTest.java b/luni/src/test/java/libcore/java/time/DurationTest.java
new file mode 100644
index 0000000..574a1d5
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/DurationTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.chrono.MinguoChronology;
+import java.time.temporal.Temporal;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link Duration}.
+ *
+ * @see tck.java.time.TCKDuration
+ * @see test.java.time.TestDuration
+ */
+public class DurationTest {
+
+ // Hardcoded maximum representable duration.
+ public static final Duration MAX_DURATION = Duration.ofSeconds(Long.MAX_VALUE, 999_999_999);
+
+ @Test
+ public void test_addTo() {
+ assertSame(Instant.EPOCH, Duration.ZERO.addTo(Instant.EPOCH));
+
+ // These tests are a little tautological, but since Duration.between is well-tested,
+ // they are still valuable.
+ assertEquals(Instant.MAX,
+ Duration.between(Instant.EPOCH, Instant.MAX).addTo(Instant.EPOCH));
+ assertEquals(Instant.MAX,
+ Duration.between(Instant.MIN, Instant.MAX).addTo(Instant.MIN));
+ assertEquals(Instant.EPOCH,
+ Duration.between(Instant.MIN, Instant.EPOCH).addTo(Instant.MIN));
+ }
+
+ @Test
+ public void test_subtractFrom() {
+ assertSame(Instant.EPOCH, Duration.ZERO.subtractFrom(Instant.EPOCH));
+ assertEquals(Instant.MIN,
+ Duration.between(Instant.MIN, Instant.EPOCH).subtractFrom(Instant.EPOCH));
+ assertEquals(Instant.MIN,
+ Duration.between(Instant.MIN, Instant.MAX).subtractFrom(Instant.MAX));
+ assertEquals(Instant.EPOCH,
+ Duration.between(Instant.EPOCH, Instant.MAX).subtractFrom(Instant.MAX));
+ }
+
+ @Test
+ public void test_addTo_exceeds() {
+ Object[][] breakingValues = new Object[][] {
+ { Instant.EPOCH, Duration.between(Instant.EPOCH, Instant.MAX).plusNanos(1) },
+ // Adding a negative duration is the same as subtracting the negated value.
+ { Instant.EPOCH, Duration.between(Instant.EPOCH, Instant.MIN).minusNanos(1) },
+ { Instant.EPOCH, Duration.between(Instant.MIN, Instant.MAX) },
+ { Instant.EPOCH, MAX_DURATION },
+ { Instant.MIN, MAX_DURATION },
+ { Instant.MAX, Duration.ofNanos(1) },
+ { LocalDateTime.MAX, Duration.ofNanos(1) },
+ { LocalDateTime.now(), MAX_DURATION },
+ { ZonedDateTime.of(LocalDateTime.MAX, ZoneOffset.UTC ), Duration.ofNanos(1) },
+ };
+
+ for (Object[] values : breakingValues) {
+ Temporal temporal = (Temporal) values[0];
+ Duration duration = (Duration) values[1];
+
+ try {
+ duration.addTo(temporal);
+ fail(" Should have failed to add " + duration + " to " + temporal);
+ } catch (DateTimeException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void test_subtractFrom_exceeds() {
+ Object[][] breakingValues = new Object[][] {
+ { Instant.EPOCH, Duration.between(Instant.MIN, Instant.EPOCH).plusNanos(1) },
+ // Subtracting a negative Duration is the same as adding the negated value.
+ { Instant.EPOCH, Duration.between(Instant.MAX, Instant.EPOCH).minusNanos(1) },
+ { Instant.EPOCH, Duration.between(Instant.MIN, Instant.MAX) },
+ { Instant.EPOCH, MAX_DURATION },
+ { Instant.MAX, MAX_DURATION },
+ { Instant.MIN, Duration.ofNanos(1) },
+ { LocalDateTime.MIN, Duration.ofNanos(1) },
+ { LocalDateTime.now(), MAX_DURATION },
+ { LocalDateTime.MAX, MAX_DURATION },
+ { ZonedDateTime.of(LocalDateTime.MIN, ZoneOffset.UTC ), Duration.ofNanos(1) },
+ };
+
+ for (Object[] values : breakingValues) {
+ Temporal temporal = (Temporal) values[0];
+ Duration duration = (Duration) values[1];
+
+ try {
+ duration.subtractFrom(temporal);
+ fail("Should have failed to subtract " + duration + " from " + temporal);
+ } catch (DateTimeException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void test_addTo_subtractFrom_unsupported() {
+ // These Temporal objects don't supports seconds/nanos.
+ // The actual values of those Temporals don't matter, only their type is checked.
+ Temporal[] unsupportedTemporals = new Temporal[] {
+ Year.now(),
+ YearMonth.now(),
+ LocalDate.now(),
+ // An arbitrary ChronoLocalDateImpl as a representative example.
+ MinguoChronology.INSTANCE.dateNow(),
+ };
+
+ Duration second = Duration.ofSeconds(1);
+ for (Temporal temporal : unsupportedTemporals) {
+ // Adding/subtracting zero should not fail.
+ assertSame(temporal, Duration.ZERO.addTo(temporal));
+ assertSame(temporal, Duration.ZERO.subtractFrom(temporal));
+
+ try {
+ second.addTo(temporal);
+ fail("Should not be able to add a duration to " + temporal);
+ } catch (UnsupportedTemporalTypeException expected) {
+ }
+ try {
+ second.subtractFrom(temporal);
+ fail("Should not be able to subtract a duration from " + temporal);
+ } catch (UnsupportedTemporalTypeException expected) {
+ }
+ }
+
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/InstantTest.java b/luni/src/test/java/libcore/java/time/InstantTest.java
new file mode 100644
index 0000000..ba31264
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/InstantTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Additional tests for {@link Instant}.
+ *
+ * @see tck.java.time.TCKInstant
+ * @see test.java.time.TestInstant
+ */
+public class InstantTest {
+
+ @Test
+ public void test_isSupported_TemporalUnit() {
+ assertEquals(false, Instant.EPOCH.isSupported((TemporalUnit) null));
+ assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.NANOS));
+ assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MICROS));
+ assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MILLIS));
+ assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.SECONDS));
+ assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MINUTES));
+ assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.HOURS));
+ assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.HALF_DAYS));
+ assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.DAYS));
+ assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.WEEKS));
+ assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.MONTHS));
+ assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.YEARS));
+ assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.DECADES));
+ assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.CENTURIES));
+ assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.MILLENNIA));
+ assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.ERAS));
+ assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.FOREVER));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/LocalDateTest.java b/luni/src/test/java/libcore/java/time/LocalDateTest.java
new file mode 100644
index 0000000..f0c8db2
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/LocalDateTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.LocalDate;
+import java.time.chrono.IsoChronology;
+
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link LocalDate}.
+ *
+ * @see tck.java.time.TCKLocalDate
+ * @see test.java.time.TestLocalDate
+ */
+public class LocalDateTest {
+
+ @Test
+ public void test_getChronology() {
+ // LocalDate always uses the IsoChronology.
+ assertSame(IsoChronology.INSTANCE, LocalDate.MIN.getChronology());
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java b/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java
new file mode 100644
index 0000000..09a258a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Additional tests for {@link OffsetDateTime}.
+ *
+ * @see tck.java.time.TCKOffsetDateTime
+ * @see test.java.time.TestOffsetDateTime
+ * @see test.java.time.TestOffsetDateTime_instants
+ */
+public class OffsetDateTimeTest {
+
+ private static final OffsetDateTime ODT =
+ OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 6, ZoneOffset.UTC);
+ // 2000-01-02T03:04:05.000000006 UTC
+
+ @Test
+ public void test_plus() {
+ // Most of the logic is in LocalDateTime, to which OffsetDateTime#plus() delegates, verify
+ // only some simple cases here. In-depth tests for LocalDateTime#plus() can be found in
+ // TCKLocalDateTime.
+ assertEquals(OffsetDateTime.of(2000, 1, 2, 4, 4, 5, 6, ZoneOffset.UTC),
+ ODT.plus(1, ChronoUnit.HOURS));
+ assertEquals(OffsetDateTime.of(2000, 1, 3, 2, 4, 5, 6, ZoneOffset.UTC),
+ ODT.plus(23, ChronoUnit.HOURS));
+ assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 5, 5, 6, ZoneOffset.UTC),
+ ODT.plus(1, ChronoUnit.MINUTES));
+ assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 5, 5, 6, ZoneOffset.UTC),
+ ODT.plus(60, ChronoUnit.SECONDS));
+ assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 1_000_006, ZoneOffset.UTC),
+ ODT.plus(1, ChronoUnit.MILLIS));
+ assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 7, ZoneOffset.UTC),
+ ODT.plus(1, ChronoUnit.NANOS));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/OffsetTimeTest.java b/luni/src/test/java/libcore/java/time/OffsetTimeTest.java
new file mode 100644
index 0000000..880343f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/OffsetTimeTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.OffsetTime;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.util.EnumSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link OffsetTime}.
+ *
+ * @see tck.java.time.TCKOffsetTime
+ * @see test.java.time.TestOffsetTime
+ */
+public class OffsetTimeTest {
+
+ private static final OffsetTime NOON_UTC = OffsetTime
+ .of(/* hour */ 12, /* minute */ 0, /* second */ 0, /* nano */ 0, ZoneOffset.UTC);
+
+ @Test
+ public void test_plus() {
+ // Most of the logic is in LocalTime, to which OffsetTime#plus() delegates, verify only some
+ // simple cases here. In-depth tests for LocalTime#plus() can be found in TCKLocalTime.
+ assertEquals(OffsetTime.of(13, 0, 0, 0, ZoneOffset.UTC),
+ NOON_UTC.plus(1, ChronoUnit.HOURS));
+ assertEquals(OffsetTime.of(11, 0, 0, 0, ZoneOffset.UTC),
+ NOON_UTC.plus(23, ChronoUnit.HOURS));
+ assertEquals(OffsetTime.of(12, 1, 0, 0, ZoneOffset.UTC),
+ NOON_UTC.plus(1, ChronoUnit.MINUTES));
+ assertEquals(OffsetTime.of(12, 1, 0, 0, ZoneOffset.UTC),
+ NOON_UTC.plus(60, ChronoUnit.SECONDS));
+ assertEquals(OffsetTime.of(12, 0, 0, 1_000_000, ZoneOffset.UTC),
+ NOON_UTC.plus(1, ChronoUnit.MILLIS));
+ assertEquals(OffsetTime.of(12, 0, 0, 1, ZoneOffset.UTC),
+ NOON_UTC.plus(1, ChronoUnit.NANOS));
+ }
+
+ @Test
+ public void test_plus_noop() {
+ assertPlusIsNoop(0, ChronoUnit.MINUTES);
+ assertPlusIsNoop(2, ChronoUnit.HALF_DAYS);
+ assertPlusIsNoop(24, ChronoUnit.HOURS);
+ assertPlusIsNoop(24 * 60, ChronoUnit.MINUTES);
+ assertPlusIsNoop(24 * 60 * 60, ChronoUnit.SECONDS);
+ assertPlusIsNoop(24 * 60 * 60 * 1_000, ChronoUnit.MILLIS);
+ assertPlusIsNoop(24 * 60 * 60 * 1_000_000_000L, ChronoUnit.NANOS);
+ }
+
+ private static void assertPlusIsNoop(long amount, TemporalUnit unit) {
+ assertSame(NOON_UTC, NOON_UTC.plus(amount, unit));
+ }
+
+ @Test
+ public void test_plus_minus_invalidUnits() {
+ for (ChronoUnit unit : EnumSet.range(ChronoUnit.DAYS, ChronoUnit.FOREVER)) {
+ try {
+ NOON_UTC.plus(1, unit);
+ fail("Adding 1 " + unit + " should have failed.");
+ } catch (UnsupportedTemporalTypeException expected) {
+ }
+ try {
+ NOON_UTC.minus(1, unit);
+ fail("Subtracting 1 " + unit + " should have failed.");
+ } catch (UnsupportedTemporalTypeException expected) {
+ }
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/PeriodTest.java b/luni/src/test/java/libcore/java/time/PeriodTest.java
new file mode 100644
index 0000000..5d312a9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/PeriodTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.Period;
+import java.time.chrono.IsoChronology;
+
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link Period}.
+ *
+ * @see tck.java.time.TCKPeriod
+ * @see test.java.time.TestPeriod
+ */
+public class PeriodTest {
+ @Test
+ public void test_getChronology() {
+ // Period always uses the IsoChronology.
+ assertSame(IsoChronology.INSTANCE, Period.ZERO.getChronology());
+ }
+
+}
diff --git a/luni/src/test/java/libcore/java/time/YearMonthTest.java b/luni/src/test/java/libcore/java/time/YearMonthTest.java
new file mode 100644
index 0000000..c4a3e79
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/YearMonthTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+import java.time.Month;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.chrono.IsoEra;
+import java.time.temporal.ChronoField;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link YearMonth}.
+ *
+ * @see tck.java.time.TCKYearMonth
+ * @see test.java.time.TestYearMonth
+ */
+public class YearMonthTest {
+
+ @Test
+ public void test_with_TemporalField_long() {
+ YearMonth ym = YearMonth.of(2000, Month.JANUARY);
+ // -1999 is actually 2000 BCE (and 0 is 1 BCE).
+ YearMonth bceYm = YearMonth.of(-1999, Month.JANUARY);
+
+ assertEquals(YearMonth.of(1000, Month.JANUARY), ym.with(ChronoField.YEAR, 1000));
+ assertEquals(YearMonth.of(-1, Month.JANUARY), ym.with(ChronoField.YEAR, -1));
+ assertEquals(YearMonth.of(2000, Month.FEBRUARY), ym.with(ChronoField.MONTH_OF_YEAR, 2));
+ assertEquals(YearMonth.of(-1999, Month.DECEMBER),
+ bceYm.with(ChronoField.MONTH_OF_YEAR, 12));
+ assertSame(ym, ym.with(ChronoField.ERA, IsoEra.CE.getValue()));
+ assertSame(bceYm, bceYm.with(ChronoField.ERA, IsoEra.BCE.getValue()));
+
+ assertEquals(bceYm, ym.with(ChronoField.ERA, IsoEra.BCE.getValue()));
+ assertEquals(ym, bceYm.with(ChronoField.ERA, IsoEra.CE.getValue()));
+ assertEquals(YearMonth.of(1, Month.JANUARY), ym.with(ChronoField.YEAR_OF_ERA, 1));
+ // Proleptic year 0 is 1 BCE.
+ assertEquals(YearMonth.of(0, Month.JANUARY), bceYm.with(ChronoField.YEAR_OF_ERA, 1));
+ assertEquals(YearMonth.of(0, Month.JANUARY), ym.with(ChronoField.PROLEPTIC_MONTH, 0));
+ assertEquals(YearMonth.of(Year.MAX_VALUE, Month.DECEMBER), ym.with(ChronoField.PROLEPTIC_MONTH, Year.MAX_VALUE * 12L + 11));
+ assertEquals(YearMonth.of(Year.MIN_VALUE, Month.JANUARY), ym.with(ChronoField.PROLEPTIC_MONTH, Year.MIN_VALUE * 12L));
+ }
+
+ @Test
+ public void test_with_TemporalField_long_invalidValue() {
+ Object[][] invalidValues = new Object[][] {
+ { ChronoField.YEAR_OF_ERA, 0 },
+ { ChronoField.YEAR_OF_ERA, Year.MAX_VALUE + 1 },
+ { ChronoField.YEAR, Year.MIN_VALUE - 1 },
+ { ChronoField.YEAR, Year.MAX_VALUE + 1 },
+ { ChronoField.ERA, -1 },
+ { ChronoField.ERA, 2 },
+ { ChronoField.MONTH_OF_YEAR, -1 },
+ { ChronoField.MONTH_OF_YEAR, 0 },
+ { ChronoField.MONTH_OF_YEAR, 13 },
+ { ChronoField.PROLEPTIC_MONTH, Year.MAX_VALUE * 12L + 12 },
+ { ChronoField.PROLEPTIC_MONTH, Year.MIN_VALUE * 12L - 1 },
+ };
+
+ YearMonth ym = YearMonth.of(2000, Month.JANUARY);
+ for (Object[] values : invalidValues) {
+ ChronoField field = (ChronoField) values[0];
+ long value = ((Number) values[1]).longValue();
+ try {
+ ym.with(field, value);
+ fail("ym.with(" + field + ", " + value + ") should have failed.");
+ } catch (DateTimeException expected) {
+ }
+ }
+
+ }
+
+ @Test
+ public void test_with_TemporalField_long_invalidField() {
+ ChronoField[] invalidFields = new ChronoField[] {
+ ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH,
+ ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR,
+ ChronoField.ALIGNED_WEEK_OF_MONTH,
+ ChronoField.ALIGNED_WEEK_OF_YEAR,
+ ChronoField.AMPM_OF_DAY,
+ ChronoField.CLOCK_HOUR_OF_AMPM,
+ ChronoField.CLOCK_HOUR_OF_DAY,
+ ChronoField.DAY_OF_MONTH,
+ ChronoField.DAY_OF_WEEK,
+ ChronoField.DAY_OF_YEAR,
+ ChronoField.EPOCH_DAY,
+ ChronoField.HOUR_OF_AMPM,
+ ChronoField.HOUR_OF_DAY,
+ ChronoField.INSTANT_SECONDS,
+ ChronoField.MICRO_OF_DAY,
+ ChronoField.MICRO_OF_SECOND,
+ ChronoField.MILLI_OF_DAY,
+ ChronoField.MILLI_OF_SECOND,
+ ChronoField.MINUTE_OF_DAY,
+ ChronoField.MINUTE_OF_HOUR,
+ ChronoField.NANO_OF_DAY,
+ ChronoField.NANO_OF_SECOND,
+ ChronoField.OFFSET_SECONDS,
+ ChronoField.SECOND_OF_DAY,
+ ChronoField.SECOND_OF_MINUTE,
+ };
+
+ YearMonth ym = YearMonth.of(2000, Month.JANUARY);
+ for (ChronoField invalidField : invalidFields) {
+ // Get a valid value to ensure we fail to due invalid field, not due to invalid value.
+ long value = invalidField.range().getMinimum();
+ try {
+ ym.with(invalidField, value);
+ fail("TemporalField.with() should not accept " + invalidField);
+ } catch (UnsupportedTemporalTypeException expected) {
+ }
+ }
+
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/YearTest.java b/luni/src/test/java/libcore/java/time/YearTest.java
new file mode 100644
index 0000000..3454391
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/YearTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.Year;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Additional tests for {@link Year}.
+ *
+ * @see tck.java.time.TCKYear
+ * @see test.java.time.TestYear
+ */
+public class YearTest {
+ @Test
+ public void test_isLeap() {
+ // More extensive tests for Year.isLeap() (which delegates to this static method) can be
+ // found in tck.java.time.TCKYear.test_isLeap()
+ assertFalse(Year.isLeap(1900));
+ assertFalse(Year.isLeap(1999));
+ assertTrue(Year.isLeap(2000));
+ assertFalse(Year.isLeap(2001));
+ assertFalse(Year.isLeap(2002));
+ assertFalse(Year.isLeap(2003));
+ assertTrue(Year.isLeap(2004));
+ assertFalse(Year.isLeap(2005));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java b/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java
new file mode 100644
index 0000000..1a37257
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoField;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link ZoneOffset}.
+ *
+ * @see tck.java.time.TCKZoneOffset
+ * @see test.java.time.TestZoneOffset
+ */
+public class ZoneOffsetTest {
+
+ private static final ZoneOffset OFFSET_P1 = ZoneOffset.ofHours(1);
+
+ @Test
+ public void test_isSupported() {
+ for (ChronoField field : ChronoField.values()) {
+ // Only OFFSET_SECONDS is supported.
+ assertEquals("isSupported(" + field + ")",
+ field == ChronoField.OFFSET_SECONDS, OFFSET_P1.isSupported(field));
+ }
+ }
+
+ @Test
+ public void test_range() {
+ assertEquals(ChronoField.OFFSET_SECONDS.range(),
+ OFFSET_P1.range(ChronoField.OFFSET_SECONDS));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_range_null() {
+ OFFSET_P1.range(null);
+ }
+
+ @Test
+ public void test_range_unsupported() {
+ for (ChronoField field : ChronoField.values()) {
+ // Only OFFSET_SECONDS is supported.
+ if (field == ChronoField.OFFSET_SECONDS) {
+ continue;
+ }
+ try {
+ OFFSET_P1.range(field);
+ fail("ZoneOffset.range(" + field + ") should have failed.");
+ } catch (UnsupportedTemporalTypeException expected) {
+ }
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java b/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java
new file mode 100644
index 0000000..09422dd
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Additional tets for {@link ZonedDateTime}.
+ *
+ * @see tck.java.time.TCKZonedDateTime
+ * @see test.java.time.TestZonedDateTime
+ */
+public class ZonedDateTimeTest {
+
+ // Europe/Vienna is UTC+2 during summer, UTC+1 during winter.
+ private static final ZoneId ZONE_VIENNA = ZoneId.of("Europe/Vienna");
+
+ // UTC+1, the offset during winter time.
+ private static final ZoneOffset OFFSET_P1 = ZoneOffset.ofHours(1);
+
+ // UTC+2, the offset during summer time.
+ private static final ZoneOffset OFFSET_P2 = ZoneOffset.ofHours(2);
+
+ // LocalDateTime during winter time (OFFSET_P1 in ZONE_VIENNA).
+ private static final LocalDateTime LDT_P1 = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0);
+
+ // LocalDateTime during summer time (OFFSET_P2 in ZONE_VIENNA).
+ private static final LocalDateTime LDT_P2 = LocalDateTime.of(2000, Month.JUNE, 1, 0, 0);
+
+ // LocalDateTime that is in a gap that occurs at the switch from winter time to summer time.
+ // This is not a valid local time in ZONE_VIENNA.
+ private static final LocalDateTime LDT_IN_GAP = LocalDateTime.of(2000, Month.MARCH, 26, 2, 30);
+
+ // LocalDateTime that is in an overlap that occurs at the switch from summer time to winter
+ // time. This LDT actually occurs twice in ZONE_VIENNA.
+ private static final LocalDateTime LDT_IN_OVERLAP =
+ LocalDateTime.of(2000, Month.OCTOBER, 29, 2, 30);
+
+ @Test
+ public void test_ofInstant() {
+ // ofInstant behaves as if it calculated an Instant from the LocalDateTime/ZoneOffset
+ // and then calling ofInstant(Instant, ZoneId). That's why "invalid" zone offsets are
+ // tolerated and basically just change how the LocalDateTime is interpreted.
+
+ // checkOfInstant(localDateTime, offset, zone, expectedDateTime, expectedOffset)
+
+ // Correct offset in summer.
+ checkOfInstant(LDT_P1, OFFSET_P1, ZONE_VIENNA, LDT_P1, OFFSET_P1);
+ // Correct offset in winter.
+ checkOfInstant(LDT_P2, OFFSET_P2, ZONE_VIENNA, LDT_P2, OFFSET_P2);
+ // "Wrong" offset in winter.
+ checkOfInstant(LDT_P1, OFFSET_P2, ZONE_VIENNA, LDT_P1.minusDays(1).withHour(23), OFFSET_P1);
+ // "Wrong" offset in summer.
+ checkOfInstant(LDT_P2, OFFSET_P1, ZONE_VIENNA, LDT_P2.withHour(1), OFFSET_P2);
+
+ // Very wrong offset in winter.
+ checkOfInstant(LDT_P1, ZoneOffset.ofHours(-10), ZONE_VIENNA, LDT_P1.withHour(11),
+ OFFSET_P1);
+
+ // Neither of those combinations exist, so they are interpreted as either before or after
+ // the gap, depending on the offset.
+ checkOfInstant(LDT_IN_GAP, OFFSET_P1, ZONE_VIENNA, LDT_IN_GAP.plusHours(1), OFFSET_P2);
+ checkOfInstant(LDT_IN_GAP, OFFSET_P2, ZONE_VIENNA, LDT_IN_GAP.minusHours(1), OFFSET_P1);
+
+ // Both combinations exist and are valid, so they produce exactly the input.
+ checkOfInstant(LDT_IN_OVERLAP, OFFSET_P1, ZONE_VIENNA, LDT_IN_OVERLAP, OFFSET_P1);
+ checkOfInstant(LDT_IN_OVERLAP, OFFSET_P2, ZONE_VIENNA, LDT_IN_OVERLAP, OFFSET_P2);
+ }
+
+ /**
+ * Assert that calling {@link ZonedDateTime#ofInstant(LocalDateTime, ZoneOffset, ZoneId)} with
+ * the first three parameters produces a sane result with the localDateTime and offset equal to
+ * the last two.
+ */
+ private static void checkOfInstant(LocalDateTime localDateTime, ZoneOffset offset,
+ ZoneId zone, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) {
+ ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(localDateTime, offset, zone);
+ String message = String.format(" for ofInstant(%s, %s, %s) = %s, ",
+ localDateTime, offset, zone, zonedDateTime);
+ // Note that localDateTime doesn't necessarily equal zoneDateTime.toLocalDateTime(),
+ // specifically when offset is not a valid offset for zone at localDateTime (or ever).
+ assertEquals("zone" + message, zone, zonedDateTime.getZone());
+
+ assertEquals("offset" + message, expectedOffset, zonedDateTime.getOffset());
+ assertEquals("localDateTime" + message, expectedDateTime, zonedDateTime.toLocalDateTime());
+ if (offset.equals(expectedOffset)) {
+ // When we get same offset, the localDateTime must be the same as the input. This
+ // assert basically just verifies that the test is written correctly.
+ assertEquals("expected localDateTime" + message,
+ expectedDateTime, zonedDateTime.toLocalDateTime());
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_ofInstant_localDateTime_null() {
+ ZonedDateTime.ofInstant(null, OFFSET_P1, ZONE_VIENNA);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_ofInstant_offset_null() {
+ ZonedDateTime.ofInstant(LDT_P1, null, ZONE_VIENNA);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_ofInstant_zone_null() {
+ ZonedDateTime.ofInstant(LDT_P1, OFFSET_P1, null);
+ }
+
+ @Test
+ public void test_ofLocal() {
+ // checkOfLocal(localDateTime, zone, preferredOffset, expectedDateTime, expectedOffset)
+
+ // Correct offset in summer.
+ checkOfLocal(LDT_P1, ZONE_VIENNA, OFFSET_P1, LDT_P1, OFFSET_P1);
+ // Correct offset in winter.
+ checkOfLocal(LDT_P2, ZONE_VIENNA, OFFSET_P2, LDT_P2, OFFSET_P2);
+ // "Wrong" offset in winter.
+ checkOfLocal(LDT_P1, ZONE_VIENNA, OFFSET_P2, LDT_P1, OFFSET_P1);
+ // "Wrong" offset in summer.
+ checkOfLocal(LDT_P2, ZONE_VIENNA, OFFSET_P1, LDT_P2, OFFSET_P2);
+ // Very wrong offset in winter.
+ checkOfLocal(LDT_P1, ZONE_VIENNA, ZoneOffset.ofHours(-10), LDT_P1, OFFSET_P1);
+
+ // Neither of those combinations exist, so they are interpreted as after the gap.
+ checkOfLocal(LDT_IN_GAP, ZONE_VIENNA, OFFSET_P1, LDT_IN_GAP.plusHours(1), OFFSET_P2);
+ checkOfLocal(LDT_IN_GAP, ZONE_VIENNA, OFFSET_P2, LDT_IN_GAP.plusHours(1), OFFSET_P2);
+
+ // Both combinations exist and are valid, so they produce exactly the input.
+ checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, OFFSET_P1, LDT_IN_OVERLAP, OFFSET_P1);
+ checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, OFFSET_P2, LDT_IN_OVERLAP, OFFSET_P2);
+
+ // Passing in null for preferredOffset will be biased to the offset before the transition.
+ checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, /* preferredOffset */ null,
+ LDT_IN_OVERLAP, OFFSET_P2);
+ // Passing in an invalid offset will be biased to the offset before the transition.
+ checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, ZoneOffset.ofHours(10),
+ LDT_IN_OVERLAP, OFFSET_P2);
+ }
+
+ /**
+ * Assert that calling {@link ZonedDateTime#ofLocal(LocalDateTime, ZoneId, ZoneOffset)} with
+ * the first three parameters produces a sane result with the localDateTime, and offset equal
+ * to the last two.
+ */
+ private static void checkOfLocal(LocalDateTime localDateTime, ZoneId zone,
+ ZoneOffset preferredOffset, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) {
+ ZonedDateTime zonedDateTime = ZonedDateTime.ofLocal(localDateTime, zone, preferredOffset);
+ String message = String.format(" for ofLocal(%s, %s, %s) = %s, ",
+ localDateTime, zone, preferredOffset, zonedDateTime);
+ // Note that localDateTime doesn't necessarily equal zoneDateTime.toLocalDateTime(),
+ // specifically when offset is not a valid offset for zone at localDateTime (or ever).
+ assertEquals("zone" + message, zone, zonedDateTime.getZone());
+ assertEquals("offset" + message, expectedOffset, zonedDateTime.getOffset());
+ assertEquals("localDateTime" + message, expectedDateTime, zonedDateTime.toLocalDateTime());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_ofLocal_localDateTime_null() {
+ ZonedDateTime.ofLocal(null, ZONE_VIENNA, OFFSET_P1);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_ofLocal_zone_null() {
+ ZonedDateTime.ofLocal(LDT_P1, null, OFFSET_P1);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java
new file mode 100644
index 0000000..37fdd7c
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.chrono.AbstractChronology;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Chronology;
+import java.time.chrono.Era;
+import java.time.chrono.IsoChronology;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.ValueRange;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Additional tests for {@link Chronology}.
+ *
+ * @see tck.java.time.chrono.TCKChronology
+ */
+public class ChronologyTest {
+
+ @Test
+ public void test_compareTo() {
+ Set<Chronology> chronologies = new LinkedHashSet<>(Chronology.getAvailableChronologies());
+ chronologies.add(new DummyChronology("aaa", "z aaa"));
+ chronologies.add(new DummyChronology("zzz", "a zzz"));
+
+ // Check for comparison of each chronology with each other (including itself).
+ for (Chronology c1 : chronologies) {
+ for (Chronology c2 : chronologies) {
+ assertComparesAccordingToId(c1, c2);
+ }
+ }
+ }
+
+ private static void assertComparesAccordingToId(Chronology c1, Chronology c2) {
+ int chronologyResult = c1.compareTo(c2);
+ int idResult = c1.getId().compareTo(c2.getId());
+ // note that this message is not strictly true: if two chronologies with the same id but
+ // different parameters exist, then they should return non-zero for compareTo(). That is not
+ // possible with any of the chronologies we currently ship (as of early 2017), though.
+ assertEquals("compareTo() must match getId().compareTo()",
+ (int) Math.signum(chronologyResult), (int) Math.signum(idResult));
+ assertEquals(c1 + " and " + c2 + " compare as equal.",
+ chronologyResult == 0, c1.equals(c2));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_compareTo_null() {
+ IsoChronology.INSTANCE.compareTo(null);
+ }
+
+ /** Dummy chronology that supports only returning an id and a type. */
+ private static class DummyChronology extends AbstractChronology {
+
+ private final String id;
+
+ private final String type;
+
+ public DummyChronology(String id, String type) {
+ this.id = id;
+ this.type = type;
+ }
+
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public String getCalendarType() {
+ return type;
+ }
+
+ @Override
+ public ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ChronoLocalDate dateEpochDay(long epochDay) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ChronoLocalDate date(TemporalAccessor temporal) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isLeapYear(long prolepticYear) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int prolepticYear(Era era, int yearOfEra) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Era eraOf(int eraValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Era> eras() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ValueRange range(ChronoField field) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java
new file mode 100644
index 0000000..3a33e5b
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.chrono.HijrahChronology;
+import java.time.chrono.HijrahDate;
+import java.time.chrono.HijrahEra;
+import java.time.temporal.ChronoField;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link HijrahDate}.
+ *
+ * @see tck.java.time.chrono.TCKHijrahChronology
+ */
+public class HijrahChronologyTest {
+ @Test
+ public void test_HijrahDate_getEra() {
+ // HijrahChronology has only one valid era.
+ assertEquals(HijrahEra.AH, HijrahDate.of(1300, 1, 1).getEra());
+ }
+
+ @Test
+ public void test_HijrahDate_getLong() {
+ // 1300 is the first year in the HijrahChronology in the umalqura configuration.
+ HijrahDate date = HijrahDate.of(1300, 2, 5);
+ assertEquals(1300, date.getLong(ChronoField.YEAR_OF_ERA));
+ assertEquals(1300, date.getLong(ChronoField.YEAR));
+ assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR));
+ // Proleptic month starts with 0 for the first month of the proleptic year 0.
+ assertEquals(1300 * 12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH));
+ assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH));
+ // first month of the year 1300 has 30 days.
+ assertEquals(30 + 5, date.getLong(ChronoField.DAY_OF_YEAR));
+ assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY));
+ }
+
+ @Test
+ public void test_HijrahDate_withVariant_same() {
+ // There is currently no way of creating an alternative HijrahChronology, so only this
+ // case and the null case are tested.
+ HijrahDate date1 = HijrahDate.now();
+ HijrahDate date2 = date1.withVariant(HijrahChronology.INSTANCE);
+ assertSame(date1, date2);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_HijrahDate_withVariant_null() {
+ HijrahDate.now().withVariant(null);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java
new file mode 100644
index 0000000..2036f44
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.Year;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.chrono.IsoChronology;
+import java.time.chrono.IsoEra;
+import java.time.temporal.ChronoField;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link IsoChronology}.
+ *
+ * @see tck.java.time.chrono.TCKIsoChronology
+ */
+public class IsoChronologyTest {
+
+ @Test
+ public void test_dateYear() {
+ int[][] allValues = new int[][] {
+ // proleptic Year, dayOfYear, expectedYear, expectedMonth, expectedDayOfMonth
+ { 2017, 1, 2017, 1, 1 },
+ { 1, 365, 1, 12, 31 },
+ { 0, 32, 1, 2, 1 },
+ { -100, 1, 101, 1, 1 },
+ { 2000, 61, 2000, 3, 1 },
+ { 2000, 366, 2000, 12, 31 },
+ { Year.MAX_VALUE, 365, Year.MAX_VALUE, 12, 31 },
+ { Year.MIN_VALUE, 365, -Year.MIN_VALUE + 1, 12, 31 },
+ };
+
+ for (int[] values : allValues) {
+ LocalDate localDate = IsoChronology.INSTANCE.dateYearDay(values[0], values[1]);
+ IsoEra expectedEra = values[0] <= 0 ? IsoEra.BCE : IsoEra.CE;
+ assertEquals(expectedEra, localDate.getEra());
+ assertEquals(values[0], localDate.getYear());
+ assertEquals(values[1], localDate.getDayOfYear());
+ assertEquals(values[2], localDate.get(ChronoField.YEAR_OF_ERA));
+ assertEquals(values[3], localDate.getMonthValue());
+ assertEquals(values[4], localDate.getDayOfMonth());
+ }
+ }
+
+ @Test
+ public void test_dateYear_invalidValues() {
+ int[][] invalidValues = new int[][] {
+ { Year.MAX_VALUE + 1, 1 },
+ { Year.MIN_VALUE - 1, 1 },
+ { Integer.MAX_VALUE, 1 },
+ { Integer.MIN_VALUE, 1 },
+ { 2001, 366 },
+ { 2000, 367 },
+ { 2017, 0 },
+ { 2017, -1 },
+ };
+
+ for (int[] values : invalidValues) {
+ try {
+ LocalDate localDate = IsoChronology.INSTANCE.dateYearDay(values[0], values[1]);
+ fail(values[0] + "/" + values[1] + " should have failed, but produced "
+ + localDate);
+ } catch (DateTimeException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void test_range() {
+ for (ChronoField field : ChronoField.values()) {
+ // IsoChronology ranges should by definition be equal to the default ranges.
+ assertEquals(field.range(), IsoChronology.INSTANCE.range(field));
+ }
+ }
+
+ @Test
+ public void test_zonedDateTime() {
+ ZonedDateTime zonedDateTime = ZonedDateTime
+ .of(/* year */ 2017, /* month */ 4, /* dayOfMonth */ 1,
+ /* hour */ 15, /* minute */ 14, /* second */ 13, /* nanoOfSecond */ 12,
+ ZoneId.of("Europe/London"));
+
+ ZonedDateTime result = IsoChronology.INSTANCE
+ .zonedDateTime(zonedDateTime.toInstant(), zonedDateTime.getZone());
+ assertEquals(LocalDate.of(2017, 4, 1), result.toLocalDate());
+ assertEquals(LocalTime.of(15, 14, 13, 12), result.toLocalTime());
+ assertEquals(ZoneOffset.ofHours(1), result.getOffset());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_zonedDateTime_nullInstant() {
+ IsoChronology.INSTANCE.zonedDateTime(null, ZoneOffset.UTC);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_zonedDateTime_nullZone() {
+ IsoChronology.INSTANCE.zonedDateTime(Instant.EPOCH, null);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java
new file mode 100644
index 0000000..3c1f0cf
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.time.chrono.JapaneseChronology;
+import java.time.chrono.JapaneseDate;
+import java.time.chrono.JapaneseEra;
+import java.time.temporal.ChronoField;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link JapaneseChronology} and {@link JapaneseDate}.
+ *
+ * @see tck.java.time.chrono.TCKJapaneseChronology
+ */
+public class JapaneseChronologyTest {
+
+ @Test
+ public void test_zonedDateTime() {
+ ZonedDateTime zonedDateTime = ZonedDateTime
+ .of(2017, 4, 1, 15, 14, 13, 12, ZoneId.of("Europe/London"));
+
+ ChronoZonedDateTime<JapaneseDate> result = JapaneseChronology.INSTANCE
+ .zonedDateTime(zonedDateTime.toInstant(), zonedDateTime.getZone());
+ assertEquals(JapaneseDate.of(JapaneseEra.HEISEI, 29, 4, 1), result.toLocalDate());
+ assertEquals(LocalTime.of(15, 14, 13, 12), result.toLocalTime());
+ assertEquals(ZoneOffset.ofHours(1), result.getOffset());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_zonedDateTime_nullInstant() {
+ JapaneseChronology.INSTANCE.zonedDateTime(null, ZoneOffset.UTC);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_zonedDateTime_nullZone() {
+ JapaneseChronology.INSTANCE.zonedDateTime(Instant.EPOCH, null);
+ }
+
+ @Test
+ public void test_JapaneseDate_getChronology() {
+ assertSame(JapaneseChronology.INSTANCE, JapaneseDate.now().getChronology());
+ }
+
+ @Test
+ public void test_JapaneseDate_getEra() {
+ // pick the first january of the second year of each era, except for Meiji, because the
+ // first supported year in JapaneseChronology is Meiji 6.
+ assertEquals(JapaneseEra.MEIJI, JapaneseDate.from(LocalDate.of(1873, 1, 1)).getEra());
+ assertEquals(JapaneseEra.TAISHO, JapaneseDate.from(LocalDate.of(1913, 1, 1)).getEra());
+ assertEquals(JapaneseEra.SHOWA, JapaneseDate.from(LocalDate.of(1927, 1, 1)).getEra());
+ assertEquals(JapaneseEra.HEISEI, JapaneseDate.from(LocalDate.of(1990, 1, 1)).getEra());
+ }
+
+ @Test
+ public void test_JapaneseDate_isSupported_TemporalField() {
+ JapaneseDate date = JapaneseDate.now();
+ // all date based fields, except for the aligned week ones are supported.
+ assertEquals(false, date.isSupported(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH));
+ assertEquals(false, date.isSupported(ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR));
+ assertEquals(false, date.isSupported(ChronoField.ALIGNED_WEEK_OF_MONTH));
+ assertEquals(false, date.isSupported(ChronoField.ALIGNED_WEEK_OF_YEAR));
+ assertEquals(false, date.isSupported(ChronoField.AMPM_OF_DAY));
+ assertEquals(false, date.isSupported(ChronoField.CLOCK_HOUR_OF_AMPM));
+ assertEquals(false, date.isSupported(ChronoField.CLOCK_HOUR_OF_DAY));
+ assertEquals(true, date.isSupported(ChronoField.DAY_OF_MONTH));
+ assertEquals(true, date.isSupported(ChronoField.DAY_OF_WEEK));
+ assertEquals(true, date.isSupported(ChronoField.DAY_OF_YEAR));
+ assertEquals(true, date.isSupported(ChronoField.EPOCH_DAY));
+ assertEquals(true, date.isSupported(ChronoField.ERA));
+ assertEquals(false, date.isSupported(ChronoField.HOUR_OF_AMPM));
+ assertEquals(false, date.isSupported(ChronoField.HOUR_OF_DAY));
+ assertEquals(false, date.isSupported(ChronoField.INSTANT_SECONDS));
+ assertEquals(false, date.isSupported(ChronoField.MICRO_OF_DAY));
+ assertEquals(false, date.isSupported(ChronoField.MICRO_OF_SECOND));
+ assertEquals(false, date.isSupported(ChronoField.MILLI_OF_DAY));
+ assertEquals(false, date.isSupported(ChronoField.MILLI_OF_SECOND));
+ assertEquals(false, date.isSupported(ChronoField.MINUTE_OF_DAY));
+ assertEquals(false, date.isSupported(ChronoField.MINUTE_OF_HOUR));
+ assertEquals(true, date.isSupported(ChronoField.MONTH_OF_YEAR));
+ assertEquals(false, date.isSupported(ChronoField.NANO_OF_DAY));
+ assertEquals(false, date.isSupported(ChronoField.NANO_OF_SECOND));
+ assertEquals(false, date.isSupported(ChronoField.OFFSET_SECONDS));
+ assertEquals(true, date.isSupported(ChronoField.PROLEPTIC_MONTH));
+ assertEquals(false, date.isSupported(ChronoField.SECOND_OF_DAY));
+ assertEquals(false, date.isSupported(ChronoField.SECOND_OF_MINUTE));
+ assertEquals(true, date.isSupported(ChronoField.YEAR));
+ assertEquals(true, date.isSupported(ChronoField.YEAR_OF_ERA));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java
new file mode 100644
index 0000000..4d9e7bc
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.LocalDate;
+import java.time.chrono.MinguoChronology;
+import java.time.chrono.MinguoDate;
+import java.time.chrono.MinguoEra;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ValueRange;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link MinguoChronology} and {@link MinguoDate}.
+ *
+ * @see tck.java.time.chrono.TCKMinguoChronology
+ */
+public class MinguoChronologyTest {
+
+ // year 1 in Minguo calendar is 1912 in ISO calendar.
+ private static final int YEARS_BEHIND = 1911;
+
+ @Test
+ public void test_range() {
+ for (ChronoField field : ChronoField.values()) {
+ ValueRange expected;
+ switch (field) {
+ case PROLEPTIC_MONTH:
+ // Proleptic month values are shifted by YEARS_BEHIND * 12.
+ expected = ValueRange.of(
+ ChronoField.PROLEPTIC_MONTH.range().getMinimum() - YEARS_BEHIND * 12L,
+ ChronoField.PROLEPTIC_MONTH.range().getMaximum() - YEARS_BEHIND * 12L);
+ break;
+ case YEAR_OF_ERA:
+ // range for era ROC is 1..<yearRange.max - OFFSET>
+ // range for era before ROC is 1..<-yearRange.min + 1 + OFFSET>
+ expected = ValueRange.of(1, ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND,
+ -ChronoField.YEAR.range().getMinimum() + 1 + YEARS_BEHIND);
+ break;
+ case YEAR:
+ // Proleptic year values are shifted by YEAR.
+ expected = ValueRange.of(ChronoField.YEAR.range().getMinimum() - YEARS_BEHIND,
+ ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND);
+ break;
+ default:
+ // All other fields have the same ranges as ISO.
+ expected = field.range();
+ break;
+ }
+ assertEquals("Range of " + field, expected, MinguoChronology.INSTANCE.range(field));
+ }
+ }
+
+ @Test
+ public void test_MinguoDate_getChronology() {
+ assertSame(MinguoChronology.INSTANCE, MinguoDate.now().getChronology());
+ }
+
+ @Test
+ public void test_MinguoDate_getEra() {
+ assertEquals(MinguoEra.BEFORE_ROC, MinguoDate.of(-1, 1, 1).getEra());
+ assertEquals(MinguoEra.ROC, MinguoDate.of(1, 1, 1).getEra());
+ }
+
+ @Test
+ public void test_MinguoDate_range() {
+ MinguoDate dates[] = new MinguoDate[] {
+ MinguoDate.from(LocalDate.of(2000, 2, 1)), //February of a leap year
+ MinguoDate.from(LocalDate.of(2001, 2, 1)), //February of a non-leap year
+ MinguoDate.of(1, 2, 3),
+ MinguoDate.of(4, 5, 6),
+ MinguoDate.of(-7, 8, 9)
+ };
+
+ for (MinguoDate date : dates) {
+ // only these three ChronoFields and YEAR_OF_ERA (below) have date-dependent ranges.
+ assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_MONTH),
+ date.range(ChronoField.DAY_OF_MONTH));
+ assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_YEAR),
+ date.range(ChronoField.DAY_OF_YEAR));
+ assertEquals(LocalDate.from(date).range(ChronoField.ALIGNED_WEEK_OF_MONTH),
+ date.range(ChronoField.ALIGNED_WEEK_OF_MONTH));
+ }
+ }
+
+ @Test
+ public void test_MinguoDate_range_yeaOfEra() {
+ // YEAR_OF_ERA is the big difference to a LocalDate, all other ranges are the same.
+ assertEquals(ValueRange.of(1, ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND),
+ MinguoDate.of(1, 1, 1).range(ChronoField.YEAR_OF_ERA));
+ assertEquals(ValueRange.of(1, -ChronoField.YEAR.range().getMinimum() + 1 + YEARS_BEHIND),
+ MinguoDate.of(-1, 1, 1).range(ChronoField.YEAR_OF_ERA));
+ }
+
+ @Test
+ public void test_MinguoDate_getLong() {
+ MinguoDate date = MinguoDate.of(10, 2, 5);
+ assertEquals(10, date.getLong(ChronoField.YEAR_OF_ERA));
+ assertEquals(10, date.getLong(ChronoField.YEAR));
+ assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR));
+ assertEquals(10*12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH));
+ assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH));
+ assertEquals(31 + 5, date.getLong(ChronoField.DAY_OF_YEAR));
+ assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java
new file mode 100644
index 0000000..20e048e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.LocalDate;
+import java.time.chrono.ThaiBuddhistDate;
+import java.time.chrono.ThaiBuddhistChronology;
+import java.time.chrono.ThaiBuddhistEra;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ValueRange;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link ThaiBuddhistDate}.
+ *
+ * @see tck.java.time.chrono.TCKThaiBuddhistChronology
+ */
+public class ThaiBuddhistChronologyTest {
+
+ // year 2543 in Thai Buddhist calendar is 2000 in ISO calendar.
+ private static final int YEARS_AHEAD = 543;
+
+ @Test
+ public void test_range() {
+ for (ChronoField field : ChronoField.values()) {
+ ValueRange expected;
+ switch (field) {
+ case PROLEPTIC_MONTH:
+ // Proleptic month values are shifted by YEARS_AHEAD * 12.
+ expected = ValueRange.of(
+ ChronoField.PROLEPTIC_MONTH.range().getMinimum() + YEARS_AHEAD * 12L,
+ ChronoField.PROLEPTIC_MONTH.range().getMaximum() + YEARS_AHEAD * 12L);
+ break;
+ case YEAR_OF_ERA:
+ // range for era BE is 1..<yearRange.max - OFFSET>
+ // range for era before BE is 1..<-yearRange.min + 1 + OFFSET>
+ expected = ValueRange
+ .of(1, -ChronoField.YEAR.range().getMinimum() + 1 - YEARS_AHEAD,
+ ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD);
+ break;
+ case YEAR:
+ // Proleptic year values are shifted by YEAR.
+ expected = ValueRange.of(ChronoField.YEAR.range().getMinimum() + YEARS_AHEAD,
+ ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD);
+ break;
+ default:
+ // All other fields have the same ranges as ISO.
+ expected = field.range();
+ break;
+ }
+ assertEquals("Range of " + field, expected,
+ ThaiBuddhistChronology.INSTANCE.range(field));
+ }
+ }
+
+ @Test
+ public void test_ThaiBuddhistDate_getChronology() {
+ assertSame(ThaiBuddhistChronology.INSTANCE, ThaiBuddhistDate.now().getChronology());
+ }
+
+ @Test
+ public void test_ThaiBuddhistDate_getEra() {
+ assertEquals(ThaiBuddhistEra.BEFORE_BE, ThaiBuddhistDate.of(-1, 1, 1).getEra());
+ assertEquals(ThaiBuddhistEra.BE, ThaiBuddhistDate.of(1, 1, 1).getEra());
+ }
+
+ @Test
+ public void test_ThaiBuddhistDate_range() {
+ ThaiBuddhistDate dates[] = new ThaiBuddhistDate[] {
+ ThaiBuddhistDate.from(LocalDate.of(2000, 2, 1)), //February of a leap year
+ ThaiBuddhistDate.from(LocalDate.of(2001, 2, 1)), //February of a non-leap year
+ ThaiBuddhistDate.of(1, 2, 3),
+ ThaiBuddhistDate.of(4, 5, 6),
+ ThaiBuddhistDate.of(-7, 8, 9)
+ };
+
+ for (ThaiBuddhistDate date : dates) {
+ // only these three ChronoFields and YEAR_OF_ERA (below) have date-dependent ranges.
+ assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_MONTH),
+ date.range(ChronoField.DAY_OF_MONTH));
+ assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_YEAR),
+ date.range(ChronoField.DAY_OF_YEAR));
+ assertEquals(LocalDate.from(date).range(ChronoField.ALIGNED_WEEK_OF_MONTH),
+ date.range(ChronoField.ALIGNED_WEEK_OF_MONTH));
+ }
+ }
+
+ @Test
+ public void test_ThaiBuddhistDate_range_yeaOfEra() {
+ // YEAR_OF_ERA is the big difference to a LocalDate, all other ranges are the same.
+ assertEquals(ValueRange.of(1, ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD),
+ ThaiBuddhistDate.of(1, 1, 1).range(ChronoField.YEAR_OF_ERA));
+ assertEquals(ValueRange.of(1, -ChronoField.YEAR.range().getMinimum() + 1 - YEARS_AHEAD),
+ ThaiBuddhistDate.of(-1, 1, 1).range(ChronoField.YEAR_OF_ERA));
+ }
+
+ @Test
+ public void test_ThaiBuddhistDate_getLong() {
+ ThaiBuddhistDate date = ThaiBuddhistDate.of(10, 2, 5);
+ assertEquals(10, date.getLong(ChronoField.YEAR_OF_ERA));
+ assertEquals(10, date.getLong(ChronoField.YEAR));
+ assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR));
+ assertEquals(10 * 12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH));
+ assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH));
+ assertEquals(31 + 5, date.getLong(ChronoField.DAY_OF_YEAR));
+ assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java
new file mode 100644
index 0000000..215f483
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.time.format;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.Month;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.TemporalQueries;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link DateTimeFormatterBuilder}.
+ *
+ * @see tck.java.time.format.TCKDateTimeFormatterBuilder
+ * @see test.java.time.format.TestDateTimeFormatterBuilder
+ */
+public class DateTimeFormatterBuilderTest {
+
+ @Test
+ public void test_append_DateTimeFormatter() {
+ DateTimeFormatter formatter = new DateTimeFormatterBuilder()
+ .appendLiteral('<').append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('>')
+ .toFormatter(Locale.ROOT);
+ assertEquals("<2000-12-31>", formatter.format(LocalDate.of(2000, Month.DECEMBER, 31)));
+ }
+
+ @Test
+ public void test_appendZoneRegionId_format() {
+ DateTimeFormatter formatter =
+ new DateTimeFormatterBuilder().appendZoneRegionId().toFormatter();
+
+ assertEquals("Europe/London",
+ formatter.format(ZonedDateTime.now(ZoneId.of("Europe/London"))));
+ assertEquals("UTC",
+ formatter.format(ZonedDateTime.now(ZoneId.of("UTC"))));
+ }
+
+ @Test
+ public void test_appendZoneRegionId_format_offset() {
+ DateTimeFormatter formatter =
+ new DateTimeFormatterBuilder().appendZoneRegionId().toFormatter();
+
+ try {
+ formatter.format(OffsetDateTime.now(ZoneOffset.UTC));
+ fail("Formatted ZoneOffset using appendZoneRegionId formatter");
+ } catch (DateTimeException expected) {
+ }
+ }
+
+ @Test
+ public void test_appendZoneRegionId_parse() {
+ DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendZoneRegionId()
+ .toFormatter();
+
+ assertEquals(ZoneId.of("Europe/London"),
+ formatter.parse("Europe/London").query(TemporalQueries.zoneId()));
+ assertEquals(ZoneId.of("UTC"),
+ formatter.parse("UTC").query(TemporalQueries.zoneId()));
+ assertEquals(ZoneId.of("GMT+1"),
+ formatter.parse("GMT+01:00").query(TemporalQueries.zoneId()));
+ // Note that the JavaDoc for appendZoneRegionId() suggests that this should return a
+ // ZoneOffset, but that documentation seems to be wrong (see http://b/35665981).
+ assertEquals(ZoneId.of("UTC+01:00"),
+ formatter.parse("UTC+01:00").query(TemporalQueries.zoneId()));
+ // Parsing a "bare metal" offset without prefix will return a ZoneOffset.
+ assertEquals(ZoneOffset.ofHours(1),
+ formatter.parse("+01:00").query(TemporalQueries.zoneId()));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
new file mode 100644
index 0000000..0e57178
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.time.format;
+
+import org.junit.Test;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DecimalStyle;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+/**
+ * Additional tests for {@link DateTimeFormatter}.
+ *
+ * @see tck.java.time.format.TCKDateTimeFormatter
+ * @see test.java.time.format.TestDateTimeFormatter
+ */
+public class DateTimeFormatterTest {
+
+ @Test
+ public void test_getDecimalStyle() {
+ Locale arLocale = Locale.forLanguageTag("ar");
+ DateTimeFormatter[] formatters = new DateTimeFormatter[] {
+ DateTimeFormatter.ISO_DATE,
+ DateTimeFormatter.RFC_1123_DATE_TIME,
+ new DateTimeFormatterBuilder().toFormatter(),
+ new DateTimeFormatterBuilder().toFormatter(Locale.ROOT),
+ new DateTimeFormatterBuilder().toFormatter(Locale.ENGLISH),
+ new DateTimeFormatterBuilder().toFormatter(arLocale),
+ };
+
+ DecimalStyle arDecimalStyle = DecimalStyle.of(arLocale);
+ // Verify that the Locale ar returns a DecimalStyle other than STANDARD.
+ assertNotEquals(DecimalStyle.STANDARD, arDecimalStyle);
+
+ for (DateTimeFormatter formatter : formatters) {
+ // All DateTimeFormatters should use the standard style, unless explicitly changed.
+ assertEquals(formatter.toString(), DecimalStyle.STANDARD, formatter.getDecimalStyle());
+
+ DateTimeFormatter arStyleFormatter = formatter.withDecimalStyle(arDecimalStyle);
+ assertEquals(arStyleFormatter.toString(),
+ arDecimalStyle, arStyleFormatter.getDecimalStyle());
+
+ // Verify that calling withDecimalStyle() doesn't modify the original formatter.
+ assertEquals(formatter.toString(), DecimalStyle.STANDARD, formatter.getDecimalStyle());
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java
new file mode 100644
index 0000000..edcd782
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.time.format;
+
+import org.junit.Test;
+import java.time.format.DateTimeParseException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for {@link DateTimeParseException}.
+ */
+public class DateTimeParseExceptionTest {
+ @Test
+ public void test_constructor_message_parsedData_errorIndex() {
+ DateTimeParseException ex =
+ new DateTimeParseException("message", new StringBuilder("parsedData"), 42);
+ assertEquals("message", ex.getMessage());
+ assertEquals("parsedData", ex.getParsedString());
+ assertEquals(42, ex.getErrorIndex());
+ assertNull(ex.getCause());
+ }
+
+ @Test
+ public void test_constructor_message_parsedData_errorIndex_cause() {
+ Throwable cause = new Exception();
+ DateTimeParseException ex =
+ new DateTimeParseException("message", new StringBuilder("parsedData"), 42, cause);
+ assertEquals("message", ex.getMessage());
+ assertEquals("parsedData", ex.getParsedString());
+ assertEquals(42, ex.getErrorIndex());
+ assertSame(cause, ex.getCause());
+ }
+
+
+}
diff --git a/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java b/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java
new file mode 100644
index 0000000..fa155df
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.time.temporal;
+
+import org.junit.Test;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for {@link UnsupportedTemporalTypeException}.
+ */
+public class UnsupportedTemporalTypeExceptionTest {
+ @Test
+ public void test_constructor_message() {
+ UnsupportedTemporalTypeException ex = new UnsupportedTemporalTypeException("message");
+ assertEquals("message", ex.getMessage());
+ assertNull(ex.getCause());
+ }
+
+ @Test
+ public void test_constructor_message_cause() {
+ Throwable cause = new Exception();
+ UnsupportedTemporalTypeException ex =
+ new UnsupportedTemporalTypeException("message", cause);
+ assertEquals("message", ex.getMessage());
+ assertSame(cause, ex.getCause());
+ }
+
+
+}
diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java
new file mode 100644
index 0000000..6094afb
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.time.zone;
+
+import org.junit.Test;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.zone.ZoneOffsetTransition;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Additional tests for {@link ZoneOffsetTransition}.
+ *
+ * @see tck.java.time.zone.TCKZoneOffsetTransition
+ */
+public class ZoneOffsetTransitionTest {
+
+ @Test
+ public void test_toEpochSeconds() {
+ LocalDateTime time = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0);
+ ZoneOffset offsetP1 = ZoneOffset.ofHours(1);
+ ZoneOffset offsetP2 = ZoneOffset.ofHours(2);
+ ZoneOffsetTransition transition = ZoneOffsetTransition.of(time,
+ /* offsetBefore */ offsetP1, /* offsetAfter */ offsetP2);
+ // toEpochSeconds must match the toEpochSeconds of the original time at the "offset before".
+ assertEquals(
+ OffsetDateTime.of(time, offsetP1).toEpochSecond(),
+ transition.toEpochSecond());
+
+ }
+
+}
diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java
new file mode 100644
index 0000000..4005175
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.time.zone;
+
+import org.junit.Test;
+import java.time.zone.ZoneRulesException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for {@link ZoneRulesException}.
+ */
+public class ZoneRulesExceptionTest {
+
+ @Test
+ public void test_constructor_message() {
+ ZoneRulesException ex = new ZoneRulesException("message");
+ assertEquals("message", ex.getMessage());
+ assertNull(ex.getCause());
+ }
+
+ @Test
+ public void test_constructor_message_cause() {
+ Throwable cause = new Exception();
+ ZoneRulesException ex = new ZoneRulesException("message", cause);
+ assertEquals("message", ex.getMessage());
+ assertSame(cause, ex.getCause());
+ }
+}
diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java
new file mode 100644
index 0000000..54b5bba
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.time.zone;
+
+import org.junit.Test;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.time.ZoneOffset;
+import java.time.zone.ZoneRules;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Additional tests for {@link ZoneRules}.
+ *
+ * @see tck.java.time.zone.TCKZoneRules
+ */
+public class ZoneRulesTest {
+
+ @Test
+ public void test_of_ZoneOffset() {
+ ZoneOffset offset = ZoneOffset.MIN;
+ ZoneRules zoneRules = ZoneRules.of(offset);
+
+ assertEquals(Collections.emptyList(), zoneRules.getTransitionRules());
+ assertEquals(Collections.emptyList(), zoneRules.getTransitions());
+ assertNull(zoneRules.nextTransition(Instant.MIN));
+
+ // Check various offsets at a bunch of instants, as they should be constant.
+ Instant[] instants = new Instant[] {
+ LocalDateTime.MIN.toInstant(offset),
+ Instant.EPOCH,
+ LocalDateTime.of(2000, Month.JANUARY, 1, 1, 1).toInstant(ZoneOffset.UTC),
+ Instant.now(),
+ LocalDateTime.MAX.toInstant(offset),
+ };
+
+ for (Instant instant : instants) {
+ assertEquals(Duration.ZERO, zoneRules.getDaylightSavings(instant));
+ assertEquals(offset, zoneRules.getOffset(instant));
+ assertEquals(offset, zoneRules.getStandardOffset(instant));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, offset);
+ assertNull(zoneRules.getTransition(localDateTime));
+ assertEquals(Collections.singletonList(offset),
+ zoneRules.getValidOffsets(localDateTime));
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_of_ZoneOffset_null() {
+ ZoneRules.of(null);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java b/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java
new file mode 100644
index 0000000..5e4ac96
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.java.util;
+
+import org.junit.Before;
+import org.junit.Test;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests {@link java.util.Calendar.Builder}.
+ */
+public class CalendarBuilderTest {
+
+
+ @Test
+ public void test_default_values() {
+ Calendar.Builder builder = new Calendar.Builder();
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_setCalendarType_iso8601() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setCalendarType("iso8601");
+ // ISO 8601 represents a gregorian calendar with a specific configuration
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.setGregorianChange(new Date(Long.MIN_VALUE));
+ expected.setFirstDayOfWeek(Calendar.MONDAY);
+ expected.setMinimalDaysInFirstWeek(4);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_setCalendarType_invalid() {
+ Calendar.Builder builder = new Calendar.Builder();
+ try {
+ builder.setCalendarType(null);
+ fail("Should have thrown NPE");
+ } catch (NullPointerException expected) {}
+
+ for (String unsupported : new String[] { "buddhist", "japanese", "notACalendarType" }) {
+ try {
+ // not supported
+ builder.setCalendarType(unsupported);
+ fail("Unsupported calendar type " + unsupported + " should have thrown.");
+ } catch (IllegalArgumentException expected) {}
+ }
+ }
+
+ @Test
+ public void test_setCalendarType_reset() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setCalendarType("gregorian");
+ try {
+ builder.setCalendarType("iso8601");
+ fail("Should not accept second setCalendarType() call");
+ } catch (IllegalStateException expected) {}
+ }
+
+ @Test
+ public void test_setDate() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setDate(2000, Calendar.FEBRUARY, 3);
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.set(2000, Calendar.FEBRUARY, 3);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_setTimeOfDay() {
+ Calendar.Builder builder = new Calendar.Builder();
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ builder.setTimeOfDay(10, 11, 12);
+ expected.set(1970, Calendar.JANUARY, 1, 10, 11, 12);
+ assertEquals(expected, builder.build());
+ builder.setTimeOfDay(10, 11, 12, 13);
+ expected.set(Calendar.MILLISECOND, 13);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_setWeekDate() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setWeekDate(1, 2000, Calendar.TUESDAY);
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.setWeekDate(1, 2000, Calendar.TUESDAY);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_setLenient() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.set(Calendar.HOUR_OF_DAY, 25);
+ builder.setLenient(false);
+ try {
+ builder.build();
+ fail("Should have failed to build.");
+ } catch (IllegalArgumentException expected) {}
+ builder.setLenient(true);
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.setLenient(true);
+ expected.set(Calendar.HOUR_OF_DAY, 25);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_setLocale() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setLocale(Locale.GERMANY);
+ GregorianCalendar expected = new GregorianCalendar(Locale.GERMANY);
+ expected.clear();
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_setLocale_thTH() {
+ // See http://b/35138741
+ Calendar.Builder builder = new Calendar.Builder();
+ Locale th = new Locale("th", "TH");
+ builder.setLocale(th);
+ GregorianCalendar expected = new GregorianCalendar(th);
+ expected.clear();
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_set() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.set(Calendar.YEAR, 2000);
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.set(Calendar.YEAR, 2000);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_set_negative_field() {
+ new Calendar.Builder().set(-1, 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_set_field_too_high() {
+ new Calendar.Builder().set(Calendar.FIELD_COUNT, 1);
+ }
+
+ @Test
+ public void test_set_after_setInstant() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setInstant(0L);
+ try {
+ builder.set(Calendar.YEAR, 2000);
+ fail("Setting a field after setInstant should fail.");
+ } catch (IllegalStateException expected) {}
+ }
+
+ @Test
+ public void test_setFields() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setFields(Calendar.YEAR, 2000, Calendar.MONTH, Calendar.FEBRUARY);
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.set(Calendar.YEAR, 2000);
+ expected.set(Calendar.MONTH, Calendar.FEBRUARY);
+ assertEquals(expected, builder.build());
+
+ // field values can be re-set and order of fields matter
+ builder.setFields(Calendar.DAY_OF_WEEK_IN_MONTH, 1,
+ Calendar.DAY_OF_MONTH, 20, // this will effectively be ignored
+ Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
+ expected.set(Calendar.DAY_OF_WEEK_IN_MONTH, 1);
+ expected.set(Calendar.DAY_OF_MONTH, 20);
+ expected.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
+ assertEquals(expected, builder.build());
+ // 20th February 2000 would have been a Sunday, but we set the DOW last.
+ assertEquals(Calendar.WEDNESDAY, builder.build().get(Calendar.DAY_OF_WEEK));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_setFields_null() {
+ new Calendar.Builder().setFields(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_setFields_oddNumberOfArguments() {
+ new Calendar.Builder().setFields(Calendar.YEAR);
+ }
+
+ @Test
+ public void test_setFields_after_setInstant() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setInstant(0L);
+ try {
+ builder.setFields(Calendar.YEAR, 2000);
+ fail("Setting a field after setInstant should fail.");
+ } catch (IllegalStateException expected) {}
+ }
+
+ @Test
+ public void test_setInstant() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setInstant(Long.MIN_VALUE);
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.setTimeInMillis(Long.MIN_VALUE);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test
+ public void test_setInstant_after_set() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.set(Calendar.YEAR, 2000);
+ try {
+ builder.setInstant(0L);
+ fail("Setting the instant after setting a field should fail.");
+ } catch (IllegalStateException expected) {}
+ }
+
+ @Test
+ public void test_setInstant_Date() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setInstant(new Date(Long.MAX_VALUE));
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.setTimeInMillis(Long.MAX_VALUE);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_setInstant_Date_null() {
+ new Calendar.Builder().setInstant(null);
+ }
+
+ @Test
+ public void test_setTimeZone() {
+ TimeZone london = TimeZone.getTimeZone("Europe/London");
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setTimeZone(london);
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.setTimeZone(london);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_setTimeZone_null() {
+ new Calendar.Builder().setTimeZone(null);
+ }
+
+ @Test
+ public void test_setWeekDefinition() {
+ Calendar.Builder builder = new Calendar.Builder();
+ builder.setWeekDefinition(Calendar.TUESDAY, 7);
+ GregorianCalendar expected = new GregorianCalendar();
+ expected.clear();
+ expected.setFirstDayOfWeek(Calendar.TUESDAY);
+ expected.setMinimalDaysInFirstWeek(7);
+ assertEquals(expected, builder.build());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_setWeekDefinition_invalid_first_dow() {
+ new Calendar.Builder().setWeekDefinition(-1, 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void test_setWeekDefinition_invalid_minimum_days() {
+ new Calendar.Builder().setWeekDefinition(Calendar.WEDNESDAY, 8);
+ }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/DateTest.java b/luni/src/test/java/libcore/java/util/DateTest.java
index 6185f12..df86a38 100644
--- a/luni/src/test/java/libcore/java/util/DateTest.java
+++ b/luni/src/test/java/libcore/java/util/DateTest.java
@@ -81,49 +81,63 @@
}
/**
- * The minimum long value below which {@link Instant#toEpochMilli()} will
- * throw is not clearly documented. This test discovers if that minimum
- * value ever changes, and also checks that it is also the minimum Instant
- * (at a millisecond boundary) that can be converted to a Date.
+ * Test that conversion between Date and Instant works when the
+ * Instant is based on a millisecond value (and thus can be
+ * represented as a Date).
*/
- public void test_convertFromInstant_lowerBound() {
- // smallest millisecond Instant that can be converted to Date
- long minConvertible = -9223372036854775000L;
+ public void test_convertFromAndToInstant_milliseconds() {
+ check_convertFromAndToInstant_milliseconds(Long.MIN_VALUE);
+ check_convertFromAndToInstant_milliseconds(Long.MAX_VALUE);
- // show that this value is < 1 sec away from Long.MIN_VALUE
- assertEquals(Long.MIN_VALUE + 808, minConvertible);
-
- Instant inBound = Instant.ofEpochMilli(minConvertible);
- assertEquals(new Date(minConvertible), Date.from(inBound));
- assertEquals(minConvertible, inBound.toEpochMilli());
-
- Instant outOfBound = Instant.ofEpochMilli(minConvertible - 1);
- try {
- Date.from(outOfBound);
- fail();
- } catch (IllegalArgumentException expected) {
- assertEquals(ArithmeticException.class, expected.getCause().getClass());
- }
-
- try {
- outOfBound.toEpochMilli();
- fail();
- } catch (ArithmeticException expected) {
-
- }
+ check_convertFromAndToInstant_milliseconds(-1);
+ check_convertFromAndToInstant_milliseconds(0);
+ check_convertFromAndToInstant_milliseconds(123456789);
}
- public void test_convertFromInstant_upperBound() {
- Date.from(Instant.ofEpochMilli(Long.MAX_VALUE));
+ private static void check_convertFromAndToInstant_milliseconds(long millis) {
+ assertEquals(new Date(millis), Date.from(Instant.ofEpochMilli(millis)));
+ assertEquals(new Date(millis).toInstant(), Instant.ofEpochMilli(millis));
+ }
- Date.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 0));
- Date.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 999999999));
- Instant outOfBound = Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1, 0);
+ /**
+ * Checks the minimum/maximum Instant values (based on seconds and
+ * nanos) that can be converted to a Date, i.e. that can be converted
+ * to milliseconds without overflowing a long. Note that the rounding
+ * is such that the lower bound is exactly Long.MIN_VALUE msec whereas
+ * the upper bound is 999,999 nanos beyond Long.MAX_VALUE msec. This
+ * makes some sense in that the magnitude of the upper/lower bound
+ * nanos differ only by 1, just like the magnitude of Long.MIN_VALUE /
+ * MAX_VALUE differ only by 1.
+ */
+ public void test_convertFromInstant_secondsAndNanos() {
+ // Documentation for how the below bounds relate to long boundaries for milliseconds
+ assertEquals(-808, Long.MIN_VALUE % 1000);
+ assertEquals(807, Long.MAX_VALUE % 1000);
+
+ // Lower bound
+ long minSecond = Long.MIN_VALUE / 1000;
+ Date.from(Instant.ofEpochSecond(minSecond));
+ // This instant exactly corresponds to Long.MIN_VALUE msec because
+ // Long.MIN_VALUE % 1000 == -808 == (-1000 + 192)
+ Date.from(Instant.ofEpochSecond(minSecond - 1, 192000000));
+ assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(minSecond - 1, 0));
+ assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(minSecond - 1, 191999999));
+
+ // Upper bound
+ long maxSecond = Long.MAX_VALUE / 1000;
+ Date.from(Instant.ofEpochSecond(maxSecond, 0));
+ // This Instant is 999,999 nanos beyond Long.MAX_VALUE msec because
+ // (Long.MAX_VALUE % 1000) == 807
+ Date.from(Instant.ofEpochSecond(maxSecond, 807999999));
+ assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(maxSecond + 1, 0));
+ assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(maxSecond, 808000000));
+ }
+
+ private static void assertArithmeticOverflowDateFrom(Instant instant) {
try {
- Date.from(outOfBound);
- fail();
+ Date.from(instant);
+ fail(instant + " should not have been convertible to Date");
} catch (IllegalArgumentException expected) {
- assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
}
diff --git a/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java b/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java
index 750b8db..3345ed5 100644
--- a/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java
@@ -16,6 +16,9 @@
package libcore.java.util;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
@@ -297,6 +300,21 @@
assertEquals(3600 * 1000, calendar.getTimeZone().getRawOffset()); // in milliseconds
}
+ public void test_fromZonedDateTime_invalidValues() {
+ ZoneId gmt = ZoneId.of("GMT");
+ ZonedDateTime[] invalidValues = {
+ ZonedDateTime.of(LocalDateTime.MAX, gmt),
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.MAX_VALUE).plusMillis(1), gmt),
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.MIN_VALUE).minusMillis(1), gmt),
+ ZonedDateTime.of(LocalDateTime.MAX, gmt) };
+ for (ZonedDateTime invalidValue : invalidValues) {
+ try {
+ GregorianCalendar.from(invalidValue);
+ fail("GregorianCalendar.from() should have failed with " + invalidValue);
+ } catch (IllegalArgumentException expected) {}
+ }
+ }
+
public void test_toZonedDateTime() {
TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris");
GregorianCalendar calendar = new GregorianCalendar(timeZone);
diff --git a/luni/src/test/java/libcore/java/util/HashtableTest.java b/luni/src/test/java/libcore/java/util/HashtableTest.java
index 4a61147..b7a3202 100644
--- a/luni/src/test/java/libcore/java/util/HashtableTest.java
+++ b/luni/src/test/java/libcore/java/util/HashtableTest.java
@@ -20,6 +20,11 @@
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ByteArrayInputStream;
+import java.lang.reflect.Field;
public class HashtableTest extends junit.framework.TestCase {
@@ -130,4 +135,30 @@
} catch (ClassNotFoundException expected) {
}
}
+
+ public void test_deserializedArrayLength() throws Exception {
+ final float loadFactor = 0.75f;
+ final int entriesCount = 100;
+ // Create table
+ Hashtable<Integer, Integer> hashtable1 = new Hashtable<>(1, loadFactor);
+ for (int i = 0; i < entriesCount; i++) {
+ hashtable1.put(i, 1);
+ }
+
+ // Serialize and deserialize
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+ oos.writeObject(hashtable1);
+ }
+ Hashtable<Integer, Integer> hashtable2 =
+ (Hashtable<Integer, Integer>) new ObjectInputStream(
+ new ByteArrayInputStream(bos.toByteArray())).readObject();
+
+ // Check that table size is >= min expected size. Due to a bug in
+ // Hashtable deserialization this wasn't the case.
+ Field tableField = Hashtable.class.getDeclaredField("table");
+ tableField.setAccessible(true);
+ Object[] table2 = (Object[]) tableField.get(hashtable2);
+ assertTrue(table2.length >= (entriesCount / loadFactor));
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index f24e0b7..2b3347c 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -157,6 +157,9 @@
if (algorithm.startsWith("AES/")) {
return "AES";
}
+ if (algorithm.startsWith("AES_128/") || algorithm.startsWith("AES_256/")) {
+ return "AES";
+ }
if (algorithm.equals("GCM")) {
return "AES";
}
@@ -273,6 +276,11 @@
key = skf.generateSecret(new PBEKeySpec("secret".toCharArray()));
} else {
KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(algorithm));
+ if (algorithm.startsWith("AES_256/")) {
+ // This is the 256-bit constrained version, so we have to switch from the
+ // default of 128-bit keys.
+ kg.init(256);
+ }
key = kg.generateKey();
}
} catch (Exception e) {
@@ -333,6 +341,20 @@
setExpectedBlockSize("AES/OFB/PKCS5PADDING", 16);
setExpectedBlockSize("AES/OFB/PKCS7PADDING", 16);
setExpectedBlockSize("AES/OFB/NOPADDING", 16);
+ setExpectedBlockSize("AES_128/CBC/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES_128/CBC/PKCS7PADDING", 16);
+ setExpectedBlockSize("AES_128/CBC/NOPADDING", 16);
+ setExpectedBlockSize("AES_128/ECB/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES_128/ECB/PKCS7PADDING", 16);
+ setExpectedBlockSize("AES_128/ECB/NOPADDING", 16);
+ setExpectedBlockSize("AES_128/GCM/NOPADDING", 16);
+ setExpectedBlockSize("AES_256/CBC/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES_256/CBC/PKCS7PADDING", 16);
+ setExpectedBlockSize("AES_256/CBC/NOPADDING", 16);
+ setExpectedBlockSize("AES_256/ECB/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES_256/ECB/PKCS7PADDING", 16);
+ setExpectedBlockSize("AES_256/ECB/NOPADDING", 16);
+ setExpectedBlockSize("AES_256/GCM/NOPADDING", 16);
setExpectedBlockSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
setExpectedBlockSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
setExpectedBlockSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
@@ -490,6 +512,10 @@
setExpectedOutputSize("AES/CTS/NOPADDING", 0);
setExpectedOutputSize("AES/ECB/NOPADDING", 0);
setExpectedOutputSize("AES/OFB/NOPADDING", 0);
+ setExpectedOutputSize("AES_128/CBC/NOPADDING", 0);
+ setExpectedOutputSize("AES_128/ECB/NOPADDING", 0);
+ setExpectedOutputSize("AES_256/CBC/NOPADDING", 0);
+ setExpectedOutputSize("AES_256/ECB/NOPADDING", 0);
setExpectedOutputSize("AES", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
@@ -505,6 +531,16 @@
setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_128/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_128/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_128/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_128/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_128/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
+ setExpectedOutputSize("AES_256/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_256/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_256/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_256/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES_256/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
@@ -538,6 +574,16 @@
setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_128/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_128/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_128/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_128/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_128/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_256/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_256/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_256/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_256/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES_256/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
@@ -771,7 +817,11 @@
if (algorithm.equals("AES")
|| algorithm.equals("AES/CBC/NOPADDING")
|| algorithm.equals("AES/CTS/NOPADDING")
- || algorithm.equals("AES/ECB/NOPADDING")) {
+ || algorithm.equals("AES/ECB/NOPADDING")
+ || algorithm.equals("AES_128/CBC/NOPADDING")
+ || algorithm.equals("AES_128/ECB/NOPADDING")
+ || algorithm.equals("AES_256/CBC/NOPADDING")
+ || algorithm.equals("AES_256/ECB/NOPADDING")) {
return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT;
}
if (algorithm.equals("DESEDE")
@@ -787,7 +837,11 @@
if (algorithm.equals("AES")
|| algorithm.equals("AES/CBC/NOPADDING")
|| algorithm.equals("AES/CTS/NOPADDING")
- || algorithm.equals("AES/ECB/NOPADDING")) {
+ || algorithm.equals("AES/ECB/NOPADDING")
+ || algorithm.equals("AES_128/CBC/NOPADDING")
+ || algorithm.equals("AES_128/ECB/NOPADDING")
+ || algorithm.equals("AES_256/CBC/NOPADDING")
+ || algorithm.equals("AES_256/ECB/NOPADDING")) {
return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT;
}
if (algorithm.equals("DESEDE")
@@ -808,7 +862,9 @@
new SecureRandom().nextBytes(salt);
return new PBEParameterSpec(salt, 1024);
}
- if (algorithm.equals("AES/GCM/NOPADDING")) {
+ if (algorithm.equals("AES/GCM/NOPADDING")
+ || algorithm.equals("AES_128/GCM/NOPADDING")
+ || algorithm.equals("AES_256/GCM/NOPADDING")) {
final byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
@@ -819,7 +875,13 @@
|| algorithm.equals("AES/CFB/NOPADDING")
|| algorithm.equals("AES/CTR/NOPADDING")
|| algorithm.equals("AES/CTS/NOPADDING")
- || algorithm.equals("AES/OFB/NOPADDING")) {
+ || algorithm.equals("AES/OFB/NOPADDING")
+ || algorithm.equals("AES_128/CBC/NOPADDING")
+ || algorithm.equals("AES_128/CBC/PKCS5PADDING")
+ || algorithm.equals("AES_128/CBC/PKCS7PADDING")
+ || algorithm.equals("AES_256/CBC/NOPADDING")
+ || algorithm.equals("AES_256/CBC/PKCS5PADDING")
+ || algorithm.equals("AES_256/CBC/PKCS7PADDING")) {
final byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
@@ -849,7 +911,9 @@
}
byte[] iv = encryptCipher.getIV();
if (iv != null) {
- if ("AES/GCM/NOPADDING".equals(algorithm)) {
+ if ("AES/GCM/NOPADDING".equals(algorithm)
+ || "AES_128/GCM/NOPADDING".equals(algorithm)
+ || "AES_256/GCM/NOPADDING".equals(algorithm)) {
return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
}
return new IvParameterSpec(iv);
@@ -1269,7 +1333,9 @@
seenBaseCipherNames.add(algorithm);
} else {
final String baseCipherName = algorithm.substring(0, firstSlash);
- if (!seenBaseCipherNames.contains(baseCipherName)) {
+ if (!seenBaseCipherNames.contains(baseCipherName)
+ && !(baseCipherName.equals("AES_128")
+ || baseCipherName.equals("AES_256"))) {
seenCiphersWithModeAndPadding.add(baseCipherName);
}
if (!"AndroidOpenSSL".equals(provider.getName())) {
@@ -1362,7 +1428,7 @@
} catch (IllegalStateException expected) {
}
- // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs JCERSAPrivateKey)
+ // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs BCRSAPrivateKey)
Key encryptKey = getEncryptKey(algorithm);
final AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
@@ -1470,8 +1536,7 @@
// Test wrapping a key. Every cipher should be able to wrap. Except those that can't.
/* Bouncycastle is broken for wrapping because getIV() fails. */
- if (isSupportedForWrapping(algorithm)
- && !algorithm.equals("AES/GCM/NOPADDING") && !providerName.equals("BC")) {
+ if (isSupportedForWrapping(algorithm) && !providerName.equals("BC")) {
// Generate a small SecretKey for AES.
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
@@ -4554,6 +4619,39 @@
}
}
+ public void testAES_keyConstrained() throws Exception {
+ Provider[] providers = Security.getProviders();
+ for (Provider p : providers) {
+ for (Provider.Service s : p.getServices()) {
+ if (s.getType().equals("Cipher")) {
+ if (s.getAlgorithm().startsWith("AES_128/")) {
+ Cipher c = Cipher.getInstance(s.getAlgorithm(), p);
+ assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 128));
+ assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192));
+ assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 256));
+ } else if (s.getAlgorithm().startsWith("AES_256/")) {
+ Cipher c = Cipher.getInstance(s.getAlgorithm(), p);
+ assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 128));
+ assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192));
+ assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 256));
+ }
+ }
+ }
+ }
+ }
+
+ private boolean checkAES_keyConstraint(Cipher c, int keySize) throws Exception {
+ KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(c.getAlgorithm()));
+ kg.init(keySize);
+ SecretKey key = kg.generateKey();
+ try {
+ c.init(Cipher.ENCRYPT_MODE, key);
+ return true;
+ } catch (InvalidKeyException e) {
+ return false;
+ }
+ }
+
/**
* Several exceptions can be thrown by init. Check that in this case we throw the right one,
* as the error could fall under the umbrella of other exceptions.
diff --git a/ojluni/src/main/java/java/lang/BootstrapMethodError.java b/ojluni/src/main/java/java/lang/BootstrapMethodError.java
new file mode 100644
index 0000000..a1509a0
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/BootstrapMethodError.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+/**
+ * Thrown to indicate that an {@code invokedynamic} instruction has
+ * failed to find its bootstrap method,
+ * or the bootstrap method has failed to provide a
+ * {@linkplain java.lang.invoke.CallSite call site} with a {@linkplain java.lang.invoke.CallSite#getTarget target}
+ * of the correct {@linkplain java.lang.invoke.MethodHandle#type method type}.
+ *
+ * @author John Rose, JSR 292 EG
+ * @since 1.7
+ */
+public class BootstrapMethodError extends LinkageError {
+ private static final long serialVersionUID = 292L;
+
+ /**
+ * Constructs a {@code BootstrapMethodError} with no detail message.
+ */
+ public BootstrapMethodError() {
+ super();
+ }
+
+ /**
+ * Constructs a {@code BootstrapMethodError} with the specified
+ * detail message.
+ *
+ * @param s the detail message.
+ */
+ public BootstrapMethodError(String s) {
+ super(s);
+ }
+
+ /**
+ * Constructs a {@code BootstrapMethodError} with the specified
+ * detail message and cause.
+ *
+ * @param s the detail message.
+ * @param cause the cause, may be {@code null}.
+ */
+ public BootstrapMethodError(String s, Throwable cause) {
+ super(s, cause);
+ }
+
+ /**
+ * Constructs a {@code BootstrapMethodError} with the specified
+ * cause.
+ *
+ * @param cause the cause, may be {@code null}.
+ */
+ public BootstrapMethodError(Throwable cause) {
+ // cf. Throwable(Throwable cause) constructor.
+ super(cause == null ? null : cause.toString());
+ initCause(cause);
+ }
+}
diff --git a/ojluni/src/main/java/java/lang/Character.java b/ojluni/src/main/java/java/lang/Character.java
index a024b17..fb0d576 100644
--- a/ojluni/src/main/java/java/lang/Character.java
+++ b/ojluni/src/main/java/java/lang/Character.java
@@ -25,6 +25,7 @@
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
@@ -5475,6 +5476,7 @@
return isLowerCaseImpl(codePoint);
}
+ @FastNative
static native boolean isLowerCaseImpl(int codePoint);
/**
@@ -5542,6 +5544,7 @@
return isUpperCaseImpl(codePoint);
}
+ @FastNative
static native boolean isUpperCaseImpl(int codePoint);
@@ -5622,6 +5625,7 @@
return isTitleCaseImpl(codePoint);
}
+ @FastNative
static native boolean isTitleCaseImpl(int codePoint);
/**
@@ -5697,6 +5701,7 @@
return isDigitImpl(codePoint);
}
+ @FastNative
static native boolean isDigitImpl(int codePoint);
/**
@@ -5752,6 +5757,7 @@
return isDefinedImpl(codePoint);
}
+ @FastNative
static native boolean isDefinedImpl(int codePoint);
/**
@@ -5826,6 +5832,7 @@
return isLetterImpl(codePoint);
}
+ @FastNative
static native boolean isLetterImpl(int codePoint);
/**
@@ -5877,6 +5884,7 @@
return isLetterOrDigitImpl(codePoint);
}
+ @FastNative
static native boolean isLetterOrDigitImpl(int codePoint);
/**
@@ -5971,6 +5979,7 @@
return isAlphabeticImpl(codePoint);
}
+ @FastNative
static native boolean isAlphabeticImpl(int codePoint);
@@ -5987,6 +5996,7 @@
public static boolean isIdeographic(int codePoint) {
return isIdeographicImpl(codePoint);
}
+ @FastNative
static native boolean isIdeographicImpl(int codePoint);
/**
@@ -6206,6 +6216,7 @@
return isUnicodeIdentifierStartImpl(codePoint);
}
+ @FastNative
static native boolean isUnicodeIdentifierStartImpl(int codePoint);
/**
@@ -6272,6 +6283,7 @@
return isUnicodeIdentifierPartImpl(codePoint);
}
+ @FastNative
static native boolean isUnicodeIdentifierPartImpl(int codePoint);
/**
@@ -6339,6 +6351,7 @@
return isIdentifierIgnorableImpl(codePoint);
}
+ @FastNative
static native boolean isIdentifierIgnorableImpl(int codePoint);
/**
@@ -6410,6 +6423,7 @@
return toLowerCaseImpl(codePoint);
}
+ @FastNative
static native int toLowerCaseImpl(int codePoint);
/**
@@ -6481,6 +6495,7 @@
return toUpperCaseImpl(codePoint);
}
+ @FastNative
static native int toUpperCaseImpl(int codePoint);
/**
@@ -6542,6 +6557,7 @@
return toTitleCaseImpl(codePoint);
}
+ @FastNative
static native int toTitleCaseImpl(int codePoint);
/**
@@ -6665,6 +6681,7 @@
return digitImpl(codePoint, radix);
}
+ @FastNative
native static int digitImpl(int codePoint, int radix);
/**
@@ -6759,6 +6776,7 @@
return getNumericValueImpl(codePoint);
}
+ @FastNative
native static int getNumericValueImpl(int codePoint);
/**
@@ -6867,6 +6885,7 @@
return isSpaceCharImpl(codePoint);
}
+ @FastNative
static native boolean isSpaceCharImpl(int codePoint);
/**
@@ -6961,6 +6980,7 @@
return isWhitespaceImpl(codePoint);
}
+ @FastNative
native static boolean isWhitespaceImpl(int codePoint);
/**
@@ -7103,6 +7123,7 @@
return (type + 1);
}
+ @FastNative
static native int getTypeImpl(int codePoint);
/**
@@ -7228,6 +7249,7 @@
return Character.DIRECTIONALITY_UNDEFINED;
}
+ @FastNative
native static byte getDirectionalityImpl(int codePoint);
/**
* Determines whether the character is mirrored according to the
@@ -7271,6 +7293,7 @@
return isMirroredImpl(codePoint);
}
+ @FastNative
native static boolean isMirroredImpl(int codePoint);
/**
* Compares two {@code Character} objects numerically.
diff --git a/ojluni/src/main/java/java/lang/Class.java b/ojluni/src/main/java/java/lang/Class.java
index 839b011..debee32 100644
--- a/ojluni/src/main/java/java/lang/Class.java
+++ b/ojluni/src/main/java/java/lang/Class.java
@@ -26,6 +26,7 @@
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import com.android.dex.Dex;
import java.io.InputStream;
@@ -462,6 +463,7 @@
}
/** Called after security checks have been made. */
+ @FastNative
static native Class<?> classForName(String className, boolean shouldInitialize,
ClassLoader classLoader) throws ClassNotFoundException;
@@ -500,6 +502,7 @@
* s.checkPackageAccess()} denies access to the package
* of this class.
*/
+ @FastNative
public native T newInstance() throws InstantiationException, IllegalAccessException;
/**
@@ -743,6 +746,7 @@
return name;
}
+ @FastNative
private native String getNameNative();
/**
@@ -972,6 +976,7 @@
}
// Returns the interfaces that this proxy class directly implements.
+ @FastNative
private native Class<?>[] getProxyInterfaces();
/**
@@ -1115,6 +1120,7 @@
return null;
}
+ @FastNative
private native Method getEnclosingMethodNative();
/**
@@ -1162,6 +1168,7 @@
return getEnclosingConstructorNative();
}
+ @FastNative
private native Constructor<?> getEnclosingConstructorNative();
private boolean classNameImpliesTopLevel() {
@@ -1181,6 +1188,7 @@
* @since JDK1.1
*/
// ANDROID-CHANGED: Removed SecurityException
+ @FastNative
public native Class<?> getDeclaringClass();
/**
@@ -1191,6 +1199,7 @@
* @since 1.5
*/
// ANDROID-CHANGED: Removed SecurityException
+ @FastNative
public native Class<?> getEnclosingClass();
/**
@@ -1295,6 +1304,7 @@
* @return {@code true} if and only if this class is an anonymous class.
* @since 1.5
*/
+ @FastNative
public native boolean isAnonymousClass();
/**
@@ -1606,6 +1616,7 @@
* if name is null.
* @see #getField(String)
*/
+ @FastNative
private native Field getPublicFieldRecursive(String name);
/**
@@ -1757,6 +1768,7 @@
* @since JDK1.1
*/
// ANDROID-CHANGED: Removed SecurityException
+ @FastNative
public native Class<?>[] getDeclaredClasses();
/**
@@ -1802,6 +1814,7 @@
* @jls 8.3 Field Declarations
*/
// ANDROID-CHANGED: Removed SecurityException
+ @FastNative
public native Field[] getDeclaredFields();
/**
@@ -1811,6 +1824,7 @@
* @param publicOnly Whether to return only public fields.
* @hide
*/
+ @FastNative
public native Field[] getDeclaredFieldsUnchecked(boolean publicOnly);
/**
@@ -1882,6 +1896,7 @@
* @param publicOnly Whether to return only public methods.
* @hide
*/
+ @FastNative
public native Method[] getDeclaredMethodsUnchecked(boolean publicOnly);
/**
@@ -1931,6 +1946,7 @@
* Returns the constructor with the given parameters if it is defined by this class;
* {@code null} otherwise. This may return a non-public member.
*/
+ @FastNative
private native Constructor<?>[] getDeclaredConstructorsInternal(boolean publicOnly);
/**
@@ -1974,11 +1990,13 @@
* @jls 8.3 Field Declarations
*/
// ANDROID-CHANGED: Removed SecurityException
+ @FastNative
public native Field getDeclaredField(String name) throws NoSuchFieldException;
/**
* Returns the subset of getDeclaredFields which are public.
*/
+ @FastNative
private native Field[] getPublicDeclaredFields();
/**
@@ -2320,6 +2338,7 @@
*
* @param args the types of the parameters to the constructor.
*/
+ @FastNative
private native Constructor<T> getDeclaredConstructorInternal(Class<?>[] args);
/**
@@ -2353,8 +2372,10 @@
/**
* Returns the simple name of a member or local class, or {@code null} otherwise.
*/
+ @FastNative
private native String getInnerClassName();
+ @FastNative
private native int getInnerClassFlags(int defaultValue);
/**
@@ -2575,17 +2596,20 @@
* @since 1.8
*/
@Override
+ @FastNative
public native <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass);
/**
* @since 1.5
*/
@Override
+ @FastNative
public native Annotation[] getDeclaredAnnotations();
/**
* Returns true if the annotation exists.
*/
+ @FastNative
private native boolean isDeclaredAnnotationPresent(Class<? extends Annotation> annotationClass);
private String getSignatureAttribute() {
@@ -2600,6 +2624,7 @@
return result.toString();
}
+ @FastNative
private native String[] getSignatureAnnotation();
/**
@@ -2752,6 +2777,7 @@
* @param name the method name
* @param args the method's parameter types
*/
+ @FastNative
private native Method getDeclaredMethodInternal(String name, Class<?>[] args);
/**
diff --git a/ojluni/src/main/java/java/lang/Math.java b/ojluni/src/main/java/java/lang/Math.java
index b53d35c..3ce39d9 100644
--- a/ojluni/src/main/java/java/lang/Math.java
+++ b/ojluni/src/main/java/java/lang/Math.java
@@ -25,6 +25,7 @@
*/
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import java.util.Random;
import sun.misc.FloatConsts;
@@ -136,6 +137,7 @@
* @param a an angle, in radians.
* @return the sine of the argument.
*/
+ @FastNative
public static native double sin(double a);
/**
@@ -149,6 +151,7 @@
* @param a an angle, in radians.
* @return the cosine of the argument.
*/
+ @FastNative
public static native double cos(double a);
/**
@@ -164,6 +167,7 @@
* @param a an angle, in radians.
* @return the tangent of the argument.
*/
+ @FastNative
public static native double tan(double a);
/**
@@ -180,6 +184,7 @@
* @param a the value whose arc sine is to be returned.
* @return the arc sine of the argument.
*/
+ @FastNative
public static native double asin(double a);
/**
@@ -194,6 +199,7 @@
* @param a the value whose arc cosine is to be returned.
* @return the arc cosine of the argument.
*/
+ @FastNative
public static native double acos(double a);
/**
@@ -209,6 +215,7 @@
* @param a the value whose arc tangent is to be returned.
* @return the arc tangent of the argument.
*/
+ @FastNative
public static native double atan(double a);
/**
@@ -257,6 +264,7 @@
* @return the value <i>e</i><sup>{@code a}</sup>,
* where <i>e</i> is the base of the natural logarithms.
*/
+ @FastNative
public static native double exp(double a);
/**
@@ -276,6 +284,7 @@
* @return the value ln {@code a}, the natural logarithm of
* {@code a}.
*/
+ @FastNative
public static native double log(double a);
/**
@@ -299,6 +308,7 @@
* @return the base 10 logarithm of {@code a}.
* @since 1.5
*/
+ @FastNative
public static native double log10(double a);
/**
@@ -318,6 +328,7 @@
* @return the positive square root of {@code a}.
* If the argument is NaN or less than zero, the result is NaN.
*/
+ @FastNative
public static native double sqrt(double a);
@@ -347,6 +358,7 @@
* @return the cube root of {@code a}.
* @since 1.5
*/
+ @FastNative
public static native double cbrt(double a);
/**
@@ -371,6 +383,7 @@
* @return the remainder when {@code f1} is divided by
* {@code f2}.
*/
+ @FastNative
public static native double IEEEremainder(double f1, double f2);
/**
@@ -392,6 +405,7 @@
* floating-point value that is greater than or equal to
* the argument and is equal to a mathematical integer.
*/
+ @FastNative
public static native double ceil(double a);
/**
@@ -409,6 +423,7 @@
* floating-point value that less than or equal to the argument
* and is equal to a mathematical integer.
*/
+ @FastNative
public static native double floor(double a);
/**
@@ -426,6 +441,7 @@
* @return the closest floating-point value to {@code a} that is
* equal to a mathematical integer.
*/
+ @FastNative
public static native double rint(double a);
/**
@@ -480,6 +496,7 @@
* in polar coordinates that corresponds to the point
* (<i>x</i>, <i>y</i>) in Cartesian coordinates.
*/
+ @FastNative
public static native double atan2(double y, double x);
/**
@@ -605,6 +622,7 @@
* @param b the exponent.
* @return the value {@code a}<sup>{@code b}</sup>.
*/
+ @FastNative
public static native double pow(double a, double b);
/**
@@ -1564,6 +1582,7 @@
* @return The hyperbolic sine of {@code x}.
* @since 1.5
*/
+ @FastNative
public static native double sinh(double x);
/**
@@ -1590,6 +1609,7 @@
* @return The hyperbolic cosine of {@code x}.
* @since 1.5
*/
+ @FastNative
public static native double cosh(double x);
/**
@@ -1628,6 +1648,7 @@
* @return The hyperbolic tangent of {@code x}.
* @since 1.5
*/
+ @FastNative
public static native double tanh(double x);
/**
@@ -1655,6 +1676,7 @@
* without intermediate overflow or underflow
* @since 1.5
*/
+ @FastNative
public static native double hypot(double x, double y);
/**
@@ -1691,6 +1713,7 @@
* @return the value <i>e</i><sup>{@code x}</sup> - 1.
* @since 1.5
*/
+ @FastNative
public static native double expm1(double x);
/**
@@ -1726,6 +1749,7 @@
* log of {@code x} + 1
* @since 1.5
*/
+ @FastNative
public static native double log1p(double x);
/**
diff --git a/ojluni/src/main/java/java/lang/Object.java b/ojluni/src/main/java/java/lang/Object.java
index 811930f..e9728e0 100644
--- a/ojluni/src/main/java/java/lang/Object.java
+++ b/ojluni/src/main/java/java/lang/Object.java
@@ -26,6 +26,8 @@
package java.lang;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Class {@code Object} is the root of the class hierarchy.
* Every class has {@code Object} as a superclass. All objects,
@@ -99,16 +101,27 @@
* @see java.lang.System#identityHashCode
*/
public int hashCode() {
- int lockWord = shadow$_monitor_;
+ return identityHashCode(this);
+ }
+
+ // Android-changed: add a local helper for identityHashCode.
+ // Package-private to be used by j.l.System. We do the implementation here
+ // to avoid Object.hashCode doing a clinit check on j.l.System, and also
+ // to avoid leaking shadow$_monitor_ outside of this class.
+ /* package-private */ static int identityHashCode(Object obj) {
+ int lockWord = obj.shadow$_monitor_;
final int lockWordStateMask = 0xC0000000; // Top 2 bits.
final int lockWordStateHash = 0x80000000; // Top 2 bits are value 2 (kStateHash).
final int lockWordHashMask = 0x0FFFFFFF; // Low 28 bits.
if ((lockWord & lockWordStateMask) == lockWordStateHash) {
return lockWord & lockWordHashMask;
}
- return System.identityHashCode(this);
+ return identityHashCodeNative(obj);
}
+ @FastNative
+ private static native int identityHashCodeNative(Object obj);
+
/**
* Indicates whether some other object is "equal to" this one.
* <p>
@@ -231,6 +244,7 @@
/*
* Native helper method for cloning.
*/
+ @FastNative
private native Object internalClone();
@@ -291,6 +305,7 @@
* @see java.lang.Object#notifyAll()
* @see java.lang.Object#wait()
*/
+ @FastNative
public final native void notify();
/**
@@ -315,6 +330,7 @@
* @see java.lang.Object#notify()
* @see java.lang.Object#wait()
*/
+ @FastNative
public final native void notifyAll();
/**
@@ -468,6 +484,7 @@
* status</i> of the current thread is cleared when
* this exception is thrown.
*/
+ @FastNative
public final native void wait(long millis, int nanos) throws InterruptedException;
/**
@@ -508,6 +525,7 @@
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
*/
+ @FastNative
public final native void wait() throws InterruptedException;
/**
diff --git a/ojluni/src/main/java/java/lang/Runtime.java b/ojluni/src/main/java/java/lang/Runtime.java
index 5a02009..3d52814 100644
--- a/ojluni/src/main/java/java/lang/Runtime.java
+++ b/ojluni/src/main/java/java/lang/Runtime.java
@@ -26,6 +26,7 @@
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import java.io.*;
import java.util.StringTokenizer;
import sun.reflect.CallerSensitive;
@@ -719,6 +720,7 @@
* @return an approximation to the total amount of memory currently
* available for future allocated objects, measured in bytes.
*/
+ @FastNative
public native long freeMemory();
/**
@@ -732,6 +734,7 @@
* @return the total amount of memory currently available for current
* and future objects, measured in bytes.
*/
+ @FastNative
public native long totalMemory();
/**
@@ -743,6 +746,7 @@
* attempt to use, measured in bytes
* @since 1.4
*/
+ @FastNative
public native long maxMemory();
/**
diff --git a/ojluni/src/main/java/java/lang/StackTraceElement.java b/ojluni/src/main/java/java/lang/StackTraceElement.java
index 58738d8..e519fea 100644
--- a/ojluni/src/main/java/java/lang/StackTraceElement.java
+++ b/ojluni/src/main/java/java/lang/StackTraceElement.java
@@ -169,11 +169,27 @@
* @see Throwable#printStackTrace()
*/
public String toString() {
- return getClassName() + "." + methodName +
- (isNativeMethod() ? "(Native Method)" :
- (fileName != null && lineNumber >= 0 ?
- "(" + fileName + ":" + lineNumber + ")" :
- (fileName != null ? "("+fileName+")" : "(Unknown Source)")));
+ // Android-changed: When ART cannot find a line number, the lineNumber field is set
+ // to the dex_pc and the fileName field is set to null.
+ StringBuilder result = new StringBuilder();
+ result.append(getClassName()).append(".").append(methodName);
+ if (isNativeMethod()) {
+ result.append("(Native Method)");
+ } else if (fileName != null) {
+ if (lineNumber >= 0) {
+ result.append("(").append(fileName).append(":").append(lineNumber).append(")");
+ } else {
+ result.append("(").append(fileName).append(")");
+ }
+ } else {
+ if (lineNumber >= 0) {
+ // The line number is actually the dex pc.
+ result.append("(Unknown Source:").append(lineNumber).append(")");
+ } else {
+ result.append("(Unknown Source)");
+ }
+ }
+ return result.toString();
}
/**
diff --git a/ojluni/src/main/java/java/lang/String.java b/ojluni/src/main/java/java/lang/String.java
index 2d985ca..2b588a5 100644
--- a/ojluni/src/main/java/java/lang/String.java
+++ b/ojluni/src/main/java/java/lang/String.java
@@ -25,6 +25,7 @@
*/
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.lang.ArrayIndexOutOfBoundsException;
@@ -536,7 +537,7 @@
* object.
*/
public int length() {
- final boolean STRING_COMPRESSION_ENABLED = false;
+ final boolean STRING_COMPRESSION_ENABLED = true;
if (STRING_COMPRESSION_ENABLED) {
// For the compression purposes (save the characters as 8-bit if all characters
// are ASCII), the least significant bit of "count" is used as the compression flag.
@@ -577,10 +578,9 @@
* argument is negative or not less than the length of this
* string.
*/
+ @FastNative
public native char charAt(int index);
- native void setCharAt(int index, char c);
-
/**
* Returns the character (Unicode code point) at the specified
* index. The index refers to {@code char} values
@@ -773,6 +773,7 @@
* within the java.lang package only. The caller is responsible for
* ensuring that start >= 0 && start <= end && end <= count.
*/
+ @FastNative
native void getCharsNoCheck(int start, int end, char[] buffer, int index);
@@ -1113,6 +1114,7 @@
* value greater than {@code 0} if this string is
* lexicographically greater than the string argument.
*/
+ @FastNative
public native int compareTo(String anotherString);
/**
@@ -1504,6 +1506,7 @@
}
}
+ @FastNative
private native int fastIndexOf(int c, int start);
/**
@@ -1976,6 +1979,7 @@
: fastSubstring(beginIndex, subLen);
}
+ @FastNative
private native String fastSubstring(int start, int length);
/**
@@ -2031,6 +2035,7 @@
* @return a string that represents the concatenation of this object's
* characters followed by the string argument's characters.
*/
+ @FastNative
public native String concat(String str);
/**
@@ -2063,21 +2068,21 @@
* occurrence of {@code oldChar} with {@code newChar}.
*/
public String replace(char oldChar, char newChar) {
- String replaced = this;
if (oldChar != newChar) {
final int len = length();
for (int i = 0; i < len; ++i) {
if (charAt(i) == oldChar) {
- if (replaced == this) {
- replaced = StringFactory.newStringFromString(this);
- }
- replaced.setCharAt(i, newChar);
+ return doReplace(oldChar, newChar);
}
}
}
- return replaced;
+ return this;
}
+ // Implementation of replace(char oldChar, char newChar) called when we found a match.
+ @FastNative
+ private native String doReplace(char oldChar, char newChar);
+
/**
* Tells whether or not this string matches the given <a
* href="../util/regex/Pattern.html#sum">regular expression</a>.
@@ -2719,6 +2724,7 @@
* of this string and whose contents are initialized to contain
* the character sequence represented by this string.
*/
+ @FastNative
public native char[] toCharArray();
@@ -2991,5 +2997,6 @@
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
+ @FastNative
public native String intern();
}
diff --git a/ojluni/src/main/java/java/lang/System.java b/ojluni/src/main/java/java/lang/System.java
index 04f4465..03fa221 100644
--- a/ojluni/src/main/java/java/lang/System.java
+++ b/ojluni/src/main/java/java/lang/System.java
@@ -25,6 +25,7 @@
*/
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import android.system.ErrnoException;
import android.system.StructPasswd;
import android.system.StructUtsname;
@@ -433,6 +434,7 @@
* @exception NullPointerException if either <code>src</code> or
* <code>dest</code> is <code>null</code>.
*/
+ @FastNative
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
@@ -489,6 +491,7 @@
* The char[] specialized, unchecked, native version of
* arraycopy(). This assumes error checking has been done.
*/
+ @FastNative
private static native void arraycopyCharUnchecked(char[] src, int srcPos,
char[] dst, int dstPos, int length);
@@ -542,6 +545,7 @@
* The byte[] specialized, unchecked, native version of
* arraycopy(). This assumes error checking has been done.
*/
+ @FastNative
private static native void arraycopyByteUnchecked(byte[] src, int srcPos,
byte[] dst, int dstPos, int length);
@@ -595,6 +599,7 @@
* The short[] specialized, unchecked, native version of
* arraycopy(). This assumes error checking has been done.
*/
+ @FastNative
private static native void arraycopyShortUnchecked(short[] src, int srcPos,
short[] dst, int dstPos, int length);
@@ -648,6 +653,7 @@
* The int[] specialized, unchecked, native version of
* arraycopy(). This assumes error checking has been done.
*/
+ @FastNative
private static native void arraycopyIntUnchecked(int[] src, int srcPos,
int[] dst, int dstPos, int length);
@@ -701,6 +707,7 @@
* The long[] specialized, unchecked, native version of
* arraycopy(). This assumes error checking has been done.
*/
+ @FastNative
private static native void arraycopyLongUnchecked(long[] src, int srcPos,
long[] dst, int dstPos, int length);
@@ -754,6 +761,7 @@
* The float[] specialized, unchecked, native version of
* arraycopy(). This assumes error checking has been done.
*/
+ @FastNative
private static native void arraycopyFloatUnchecked(float[] src, int srcPos,
float[] dst, int dstPos, int length);
@@ -807,6 +815,7 @@
* The double[] specialized, unchecked, native version of
* arraycopy(). This assumes error checking has been done.
*/
+ @FastNative
private static native void arraycopyDoubleUnchecked(double[] src, int srcPos,
double[] dst, int dstPos, int length);
@@ -860,6 +869,7 @@
* The boolean[] specialized, unchecked, native version of
* arraycopy(). This assumes error checking has been done.
*/
+ @FastNative
private static native void arraycopyBooleanUnchecked(boolean[] src, int srcPos,
boolean[] dst, int dstPos, int length);
// ----- END android -----
@@ -875,7 +885,12 @@
* @return the hashCode
* @since JDK1.1
*/
- public static native int identityHashCode(Object x);
+ public static int identityHashCode(Object x) {
+ if (x == null) {
+ return 0;
+ }
+ return Object.identityHashCode(x);
+ }
/**
* System properties. The following properties are guaranteed to be defined:
diff --git a/ojluni/src/main/java/java/lang/Thread.java b/ojluni/src/main/java/java/lang/Thread.java
index 27c820e..18cff06 100644
--- a/ojluni/src/main/java/java/lang/Thread.java
+++ b/ojluni/src/main/java/java/lang/Thread.java
@@ -26,6 +26,7 @@
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
@@ -271,6 +272,7 @@
*
* @return the currently executing thread.
*/
+ @FastNative
public static native Thread currentThread();
/**
@@ -312,6 +314,7 @@
Thread.sleep(millis, 0);
}
+ @FastNative
private static native void sleep(Object lock, long millis, int nanos)
throws InterruptedException;
@@ -942,6 +945,7 @@
* @see #isInterrupted()
* @revised 6.0
*/
+ @FastNative
public static native boolean interrupted();
/**
@@ -957,6 +961,7 @@
* @see #interrupted()
* @revised 6.0
*/
+ @FastNative
public native boolean isInterrupted();
/**
@@ -2036,6 +2041,7 @@
private native int nativeGetStatus(boolean hasBeenStarted);
+ @FastNative
private native void nativeInterrupt();
/** Park states */
diff --git a/ojluni/src/main/java/java/lang/Throwable.java b/ojluni/src/main/java/java/lang/Throwable.java
index 86e2939..fe3c69d 100644
--- a/ojluni/src/main/java/java/lang/Throwable.java
+++ b/ojluni/src/main/java/java/lang/Throwable.java
@@ -25,6 +25,7 @@
*/
package java.lang;
+import dalvik.annotation.optimization.FastNative;
import java.io.*;
import java.util.*;
@@ -777,6 +778,7 @@
return this;
}
+ @FastNative
private static native Object nativeFillInStackTrace();
/**
@@ -881,6 +883,7 @@
* @throws IndexOutOfBoundsException if {@code index < 0 ||
* index >= getStackTraceDepth() }
*/
+ @FastNative
private static native StackTraceElement[] nativeGetStackTrace(Object stackState);
diff --git a/ojluni/src/main/java/java/lang/Void.java b/ojluni/src/main/java/java/lang/Void.java
index 91778b1..8311ea8 100644
--- a/ojluni/src/main/java/java/lang/Void.java
+++ b/ojluni/src/main/java/java/lang/Void.java
@@ -46,6 +46,12 @@
*/
public static final Class<Void> TYPE = lookupType();
+ // Android-changed: Upstream code would use reflection to establish the value of "void.class".
+ // ART makes a native call instead because the reflection approach could lead to initialization
+ // of TYPE with the current, i.e. uninitialized, value of TYPE due to other Android changes.
+ @dalvik.annotation.optimization.FastNative
+ private static native Class<Void> lookupType();
+ /*
@SuppressWarnings("unchecked")
private static Class<Void> lookupType() {
try {
@@ -55,6 +61,7 @@
throw new AssertionError(e);
}
}
+ */
/*
* The Void class cannot be instantiated.
diff --git a/ojluni/src/main/java/java/lang/invoke/CallSite.java b/ojluni/src/main/java/java/lang/invoke/CallSite.java
new file mode 100644
index 0000000..f9338e6
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/CallSite.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: Not using Empty
+//import sun.invoke.empty.Empty;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+
+/**
+ * A {@code CallSite} is a holder for a variable {@link MethodHandle},
+ * which is called its {@code target}.
+ * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
+ * all calls to the site's current target.
+ * A {@code CallSite} may be associated with several {@code invokedynamic}
+ * instructions, or it may be "free floating", associated with none.
+ * In any case, it may be invoked through an associated method handle
+ * called its {@linkplain #dynamicInvoker dynamic invoker}.
+ * <p>
+ * {@code CallSite} is an abstract class which does not allow
+ * direct subclassing by users. It has three immediate,
+ * concrete subclasses that may be either instantiated or subclassed.
+ * <ul>
+ * <li>If a mutable target is not required, an {@code invokedynamic} instruction
+ * may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}.
+ * <li>If a mutable target is required which has volatile variable semantics,
+ * because updates to the target must be immediately and reliably witnessed by other threads,
+ * a {@linkplain VolatileCallSite volatile call site} may be used.
+ * <li>Otherwise, if a mutable target is required,
+ * a {@linkplain MutableCallSite mutable call site} may be used.
+ * </ul>
+ * <p>
+ * A non-constant call site may be <em>relinked</em> by changing its target.
+ * The new target must have the same {@linkplain MethodHandle#type() type}
+ * as the previous target.
+ * Thus, though a call site can be relinked to a series of
+ * successive targets, it cannot change its type.
+ * <p>
+ * Here is a sample use of call sites and bootstrap methods which links every
+ * dynamic call site to print its arguments:
+<blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java -->
+static void test() throws Throwable {
+ // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
+ InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
+}
+private static void printArgs(Object... args) {
+ System.out.println(java.util.Arrays.deepToString(args));
+}
+private static final MethodHandle printArgs;
+static {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ Class thisClass = lookup.lookupClass(); // (who am I?)
+ printArgs = lookup.findStatic(thisClass,
+ "printArgs", MethodType.methodType(void.class, Object[].class));
+}
+private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
+ // ignore caller and name, but match the type:
+ return new ConstantCallSite(printArgs.asType(type));
+}
+</pre></blockquote>
+ * @author John Rose, JSR 292 EG
+ */
+abstract
+public class CallSite {
+ // Android-changed: not used.
+ // static { MethodHandleImpl.initStatics(); }
+
+ // The actual payload of this call site:
+ /*package-private*/
+ MethodHandle target; // Note: This field is known to the JVM. Do not change.
+
+ /**
+ * Make a blank call site object with the given method type.
+ * An initial target method is supplied which will throw
+ * an {@link IllegalStateException} if called.
+ * <p>
+ * Before this {@code CallSite} object is returned from a bootstrap method,
+ * it is usually provided with a more useful target method,
+ * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
+ * @throws NullPointerException if the proposed type is null
+ */
+
+ /*package-private*/
+ CallSite(MethodType type) {
+ // Android-changed: No cache for these so create uninitializedCallSite target here using
+ // method handle transformations to create a method handle that has the expected method
+ // type but throws an IllegalStateException.
+ // target = type.invokers().uninitializedCallSite();
+ this.target = MethodHandles.throwException(type.returnType(), IllegalStateException.class);
+ this.target = MethodHandles.insertArguments(
+ this.target, 0, new IllegalStateException("uninitialized call site"));
+ if (type.parameterCount() > 0) {
+ this.target = MethodHandles.dropArguments(this.target, 0, type.ptypes());
+ }
+
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
+
+ /**
+ * Make a call site object equipped with an initial target method handle.
+ * @param target the method handle which will be the initial target of the call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ /*package-private*/
+ CallSite(MethodHandle target) {
+ target.type(); // null check
+ this.target = target;
+
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
+
+ /**
+ * Make a call site object equipped with an initial target method handle.
+ * @param targetType the desired type of the call site
+ * @param createTargetHook a hook which will bind the call site to the target method handle
+ * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
+ * or if the target returned by the hook is not of the given {@code targetType}
+ * @throws NullPointerException if the hook returns a null value
+ * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
+ * @throws Throwable anything else thrown by the the hook function
+ */
+ /*package-private*/
+ CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
+ this(targetType);
+ ConstantCallSite selfCCS = (ConstantCallSite) this;
+ MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
+ checkTargetChange(this.target, boundTarget);
+ this.target = boundTarget;
+
+ // Android-changed: Using initializer method for GET_TARGET
+ // rather than complex static initializer.
+ initializeGetTarget();
+ }
+
+ /**
+ * Returns the type of this call site's target.
+ * Although targets may change, any call site's type is permanent, and can never change to an unequal type.
+ * The {@code setTarget} method enforces this invariant by refusing any new target that does
+ * not have the previous target's type.
+ * @return the type of the current target, which is also the type of any future target
+ */
+ public MethodType type() {
+ // warning: do not call getTarget here, because CCS.getTarget can throw IllegalStateException
+ return target.type();
+ }
+
+ /**
+ * Returns the target method of the call site, according to the
+ * behavior defined by this call site's specific class.
+ * The immediate subclasses of {@code CallSite} document the
+ * class-specific behaviors of this method.
+ *
+ * @return the current linkage state of the call site, its target method handle
+ * @see ConstantCallSite
+ * @see VolatileCallSite
+ * @see #setTarget
+ * @see ConstantCallSite#getTarget
+ * @see MutableCallSite#getTarget
+ * @see VolatileCallSite#getTarget
+ */
+ public abstract MethodHandle getTarget();
+
+ /**
+ * Updates the target method of this call site, according to the
+ * behavior defined by this call site's specific class.
+ * The immediate subclasses of {@code CallSite} document the
+ * class-specific behaviors of this method.
+ * <p>
+ * The type of the new target must be {@linkplain MethodType#equals equal to}
+ * the type of the old target.
+ *
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see CallSite#getTarget
+ * @see ConstantCallSite#setTarget
+ * @see MutableCallSite#setTarget
+ * @see VolatileCallSite#setTarget
+ */
+ public abstract void setTarget(MethodHandle newTarget);
+
+ void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
+ MethodType oldType = oldTarget.type();
+ MethodType newType = newTarget.type(); // null check!
+ if (!newType.equals(oldType))
+ throw wrongTargetType(newTarget, oldType);
+ }
+
+ private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
+ return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
+ }
+
+ /**
+ * Produces a method handle equivalent to an invokedynamic instruction
+ * which has been linked to this call site.
+ * <p>
+ * This method is equivalent to the following code:
+ * <blockquote><pre>
+ * MethodHandle getTarget, invoker, result;
+ * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
+ * invoker = MethodHandles.exactInvoker(this.type());
+ * result = MethodHandles.foldArguments(invoker, getTarget)
+ * </pre></blockquote>
+ *
+ * @return a method handle which always invokes this call site's current target
+ */
+ public abstract MethodHandle dynamicInvoker();
+
+ /*non-public*/ MethodHandle makeDynamicInvoker() {
+ // Android-changed: Use bindTo() rather than bindReceiver() (not implemented).
+ MethodHandle getTarget = GET_TARGET.bindTo(this);
+ MethodHandle invoker = MethodHandles.exactInvoker(this.type());
+ return MethodHandles.foldArguments(invoker, getTarget);
+ }
+
+ // Android-changed: no longer final. GET_TARGET assigned in initializeGetTarget().
+ private static MethodHandle GET_TARGET = null;
+
+ private void initializeGetTarget() {
+ // Android-changed: moved from static initializer for
+ // GET_TARGET to avoid issues with running early. Called from
+ // constructors. CallSite creation is not performance critical.
+ synchronized (CallSite.class) {
+ if (GET_TARGET == null) {
+ try {
+ GET_TARGET = IMPL_LOOKUP.
+ findVirtual(CallSite.class, "getTarget",
+ MethodType.methodType(MethodHandle.class));
+ } catch (ReflectiveOperationException e) {
+ throw new InternalError(e);
+ }
+ }
+ }
+ }
+
+ /* Android-changed: not used. */
+ // /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
+ // /*package-private*/
+ // static Empty uninitializedCallSite() {
+ // throw new IllegalStateException("uninitialized call site");
+ // }
+
+ // unsafe stuff:
+ private static final long TARGET_OFFSET;
+ static {
+ try {
+ TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+ } catch (Exception ex) { throw new Error(ex); }
+ }
+
+ /*package-private*/
+ void setTargetNormal(MethodHandle newTarget) {
+ // Android-changed: Set value directly.
+ // MethodHandleNatives.setCallSiteTargetNormal(this, newTarget);
+ target = newTarget;
+ }
+ /*package-private*/
+ MethodHandle getTargetVolatile() {
+ return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
+ }
+ /*package-private*/
+ void setTargetVolatile(MethodHandle newTarget) {
+ // Android-changed: Set value directly.
+ // MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
+ UNSAFE.putObjectVolatile(this, TARGET_OFFSET, newTarget);
+ }
+
+ /* Android-changed: not used. */
+ // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite:
+ // static CallSite makeSite(MethodHandle bootstrapMethod,
+ // // Callee information:
+ // String name, MethodType type,
+ // // Extra arguments for BSM, if any:
+ // Object info,
+ // // Caller information:
+ // Class<?> callerClass) {
+ // Object caller = IMPL_LOOKUP.in(callerClass);
+ // CallSite site;
+ // try {
+ // Object binding;
+ // info = maybeReBox(info);
+ // if (info == null) {
+ // binding = bootstrapMethod.invoke(caller, name, type);
+ // } else if (!info.getClass().isArray()) {
+ // binding = bootstrapMethod.invoke(caller, name, type, info);
+ // } else {
+ // Object[] argv = (Object[]) info;
+ // maybeReBoxElements(argv);
+ // if (3 + argv.length > 255)
+ // throw new BootstrapMethodError("too many bootstrap method arguments");
+ // MethodType bsmType = bootstrapMethod.type();
+ // if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
+ // binding = bootstrapMethod.invoke(caller, name, type, argv);
+ // else
+ // binding = MethodHandles.spreadInvoker(bsmType, 3)
+ // .invoke(bootstrapMethod, caller, name, type, argv);
+ // }
+ // //System.out.println("BSM for "+name+type+" => "+binding);
+ // if (binding instanceof CallSite) {
+ // site = (CallSite) binding;
+ // } else {
+ // throw new ClassCastException("bootstrap method failed to produce a CallSite");
+ // }
+ // if (!site.getTarget().type().equals(type))
+ // throw new WrongMethodTypeException("wrong type: "+site.getTarget());
+ // } catch (Throwable ex) {
+ // BootstrapMethodError bex;
+ // if (ex instanceof BootstrapMethodError)
+ // bex = (BootstrapMethodError) ex;
+ // else
+ // bex = new BootstrapMethodError("call site initialization exception", ex);
+ // throw bex;
+ // }
+ // return site;
+ // }
+ // private static Object maybeReBox(Object x) {
+ // if (x instanceof Integer) {
+ // int xi = (int) x;
+ // if (xi == (byte) xi)
+ // x = xi; // must rebox; see JLS 5.1.7
+ // }
+ // return x;
+ // }
+ // private static void maybeReBoxElements(Object[] xa) {
+ // for (int i = 0; i < xa.length; i++) {
+ // xa[i] = maybeReBox(xa[i]);
+ // }
+ // }
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java b/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java
new file mode 100644
index 0000000..2d9fede
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+/**
+ * A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed.
+ * An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently
+ * bound to the call site's target.
+ * @author John Rose, JSR 292 EG
+ */
+public class ConstantCallSite extends CallSite {
+ private final boolean isFrozen;
+
+ /**
+ * Creates a call site with a permanent target.
+ * @param target the target to be permanently associated with this call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ public ConstantCallSite(MethodHandle target) {
+ super(target);
+ isFrozen = true;
+ }
+
+ /**
+ * Creates a call site with a permanent target, possibly bound to the call site itself.
+ * <p>
+ * During construction of the call site, the {@code createTargetHook} is invoked to
+ * produce the actual target, as if by a call of the form
+ * {@code (MethodHandle) createTargetHook.invoke(this)}.
+ * <p>
+ * Note that user code cannot perform such an action directly in a subclass constructor,
+ * since the target must be fixed before the {@code ConstantCallSite} constructor returns.
+ * <p>
+ * The hook is said to bind the call site to a target method handle,
+ * and a typical action would be {@code someTarget.bindTo(this)}.
+ * However, the hook is free to take any action whatever,
+ * including ignoring the call site and returning a constant target.
+ * <p>
+ * The result returned by the hook must be a method handle of exactly
+ * the same type as the call site.
+ * <p>
+ * While the hook is being called, the new {@code ConstantCallSite}
+ * object is in a partially constructed state.
+ * In this state,
+ * a call to {@code getTarget}, or any other attempt to use the target,
+ * will result in an {@code IllegalStateException}.
+ * It is legal at all times to obtain the call site's type using the {@code type} method.
+ *
+ * @param targetType the type of the method handle to be permanently associated with this call site
+ * @param createTargetHook a method handle to invoke (on the call site) to produce the call site's target
+ * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
+ * or if the target returned by the hook is not of the given {@code targetType}
+ * @throws NullPointerException if the hook returns a null value
+ * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
+ * @throws Throwable anything else thrown by the the hook function
+ */
+ protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
+ super(targetType, createTargetHook);
+ isFrozen = true;
+ }
+
+ /**
+ * Returns the target method of the call site, which behaves
+ * like a {@code final} field of the {@code ConstantCallSite}.
+ * That is, the the target is always the original value passed
+ * to the constructor call which created this instance.
+ *
+ * @return the immutable linkage state of this call site, a constant method handle
+ * @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
+ */
+ @Override public final MethodHandle getTarget() {
+ if (!isFrozen) throw new IllegalStateException();
+ return target;
+ }
+
+ /**
+ * Always throws an {@link UnsupportedOperationException}.
+ * This kind of call site cannot change its target.
+ * @param ignore a new target proposed for the call site, which is ignored
+ * @throws UnsupportedOperationException because this kind of call site cannot change its target
+ */
+ @Override public final void setTarget(MethodHandle ignore) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns this call site's permanent target.
+ * Since that target will never change, this is a correct implementation
+ * of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}.
+ * @return the immutable linkage state of this call site, a constant method handle
+ * @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return getTarget();
+ }
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
index c41e0c2..6e414d0 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
@@ -139,7 +139,7 @@
MethodHandleImpl directTarget = getMethodHandleImpl(target);
// Given that this is specified to be an "unchecked" crack, we can directly allocate
// a member from the underlying ArtField / Method and bypass all associated access checks.
- return (T) directTarget.getMemberInternal();
+ return expected.cast(directTarget.getMemberInternal());
}
/**
@@ -665,6 +665,9 @@
*/
static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, PUBLIC);
+ /** Package-private version of lookup which is trusted. */
+ static final Lookup IMPL_LOOKUP = new Lookup(Object.class, ALL_MODES);
+
private static void checkUnprivilegedlookupClass(Class<?> lookupClass, int allowedModes) {
String name = lookupClass.getName();
if (name.startsWith("java.lang.invoke."))
@@ -1126,6 +1129,10 @@
private MethodHandle findSpecial(Method method, MethodType type,
Class<?> refc, Class<?> specialCaller)
throws IllegalAccessException {
+ if (Modifier.isStatic(method.getModifiers())) {
+ throw new IllegalAccessException("expected a non-static method:" + method);
+ }
+
if (Modifier.isPrivate(method.getModifiers())) {
// Since this is a private method, we'll need to also make sure that the
// lookup class is the same as the refering class. We've already checked that
@@ -1656,7 +1663,7 @@
}
}
- public void throwMakeAccessException(String message, Object from) throws
+ private void throwMakeAccessException(String message, Object from) throws
IllegalAccessException{
message = message + ": "+ toString();
if (from != null) message += ", from " + from;
diff --git a/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java b/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java
new file mode 100644
index 0000000..37ca324
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: not used.
+// import java.util.concurrent.atomic.AtomicInteger;
+
+// Android-changed: removed references to MutableCallSite.syncAll().
+/**
+ * A {@code MutableCallSite} is a {@link CallSite} whose target variable
+ * behaves like an ordinary field.
+ * An {@code invokedynamic} instruction linked to a {@code MutableCallSite} delegates
+ * all calls to the site's current target.
+ * The {@linkplain CallSite#dynamicInvoker dynamic invoker} of a mutable call site
+ * also delegates each call to the site's current target.
+ * <p>
+ * Here is an example of a mutable call site which introduces a
+ * state variable into a method handle chain.
+ * <!-- JavaDocExamplesTest.testMutableCallSite -->
+ * <blockquote><pre>
+MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
+MethodHandle MH_name = name.dynamicInvoker();
+MethodType MT_str1 = MethodType.methodType(String.class);
+MethodHandle MH_upcase = MethodHandles.lookup()
+ .findVirtual(String.class, "toUpperCase", MT_str1);
+MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
+name.setTarget(MethodHandles.constant(String.class, "Rocky"));
+assertEquals("ROCKY", (String) worker1.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Fred"));
+assertEquals("FRED", (String) worker1.invokeExact());
+// (mutation can be continued indefinitely)
+ * </pre></blockquote>
+ * <p>
+ * The same call site may be used in several places at once.
+ * <blockquote><pre>
+MethodType MT_str2 = MethodType.methodType(String.class, String.class);
+MethodHandle MH_cat = lookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
+MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
+assertEquals("Fred, dear?", (String) worker2.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Wilma"));
+assertEquals("WILMA", (String) worker1.invokeExact());
+assertEquals("Wilma, dear?", (String) worker2.invokeExact());
+ * </pre></blockquote>
+ * <p>
+ * <em>Non-synchronization of target values:</em>
+ * A write to a mutable call site's target does not force other threads
+ * to become aware of the updated value. Threads which do not perform
+ * suitable synchronization actions relative to the updated call site
+ * may cache the old target value and delay their use of the new target
+ * value indefinitely.
+ * (This is a normal consequence of the Java Memory Model as applied
+ * to object fields.)
+ * <p>
+ * For target values which will be frequently updated, consider using
+ * a {@linkplain VolatileCallSite volatile call site} instead.
+ * @author John Rose, JSR 292 EG
+ */
+public class MutableCallSite extends CallSite {
+ /**
+ * Creates a blank call site object with the given method type.
+ * The initial target is set to a method handle of the given type
+ * which will throw an {@link IllegalStateException} if called.
+ * <p>
+ * The type of the call site is permanently set to the given type.
+ * <p>
+ * Before this {@code CallSite} object is returned from a bootstrap method,
+ * or invoked in some other manner,
+ * it is usually provided with a more useful target method,
+ * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
+ * @param type the method type that this call site will have
+ * @throws NullPointerException if the proposed type is null
+ */
+ public MutableCallSite(MethodType type) {
+ super(type);
+ }
+
+ /**
+ * Creates a call site object with an initial target method handle.
+ * The type of the call site is permanently set to the initial target's type.
+ * @param target the method handle that will be the initial target of the call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ public MutableCallSite(MethodHandle target) {
+ super(target);
+ }
+
+ /**
+ * Returns the target method of the call site, which behaves
+ * like a normal field of the {@code MutableCallSite}.
+ * <p>
+ * The interactions of {@code getTarget} with memory are the same
+ * as of a read from an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, the current thread may choose to reuse the result
+ * of a previous read of the target from memory, and may fail to see
+ * a recent update to the target by another thread.
+ *
+ * @return the linkage state of this call site, a method handle which can change over time
+ * @see #setTarget
+ */
+ @Override public final MethodHandle getTarget() {
+ return target;
+ }
+
+ /**
+ * Updates the target method of this call site, as a normal variable.
+ * The type of the new target must agree with the type of the old target.
+ * <p>
+ * The interactions with memory are the same
+ * as of a write to an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, unrelated threads may fail to see the updated target
+ * until they perform a read from memory.
+ * Stronger guarantees can be created by putting appropriate operations
+ * into the bootstrap method and/or the target methods used
+ * at any given call site.
+ *
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see #getTarget
+ */
+ @Override public void setTarget(MethodHandle newTarget) {
+ checkTargetChange(this.target, newTarget);
+ setTargetNormal(newTarget);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return makeDynamicInvoker();
+ }
+
+ // Android-changed: not exposing incomplete implementation.
+ // /**
+ // * Performs a synchronization operation on each call site in the given array,
+ // * forcing all other threads to throw away any cached values previously
+ // * loaded from the target of any of the call sites.
+ // * <p>
+ // * This operation does not reverse any calls that have already started
+ // * on an old target value.
+ // * (Java supports {@linkplain java.lang.Object#wait() forward time travel} only.)
+ // * <p>
+ // * The overall effect is to force all future readers of each call site's target
+ // * to accept the most recently stored value.
+ // * ("Most recently" is reckoned relative to the {@code syncAll} itself.)
+ // * Conversely, the {@code syncAll} call may block until all readers have
+ // * (somehow) decached all previous versions of each call site's target.
+ // * <p>
+ // * To avoid race conditions, calls to {@code setTarget} and {@code syncAll}
+ // * should generally be performed under some sort of mutual exclusion.
+ // * Note that reader threads may observe an updated target as early
+ // * as the {@code setTarget} call that install the value
+ // * (and before the {@code syncAll} that confirms the value).
+ // * On the other hand, reader threads may observe previous versions of
+ // * the target until the {@code syncAll} call returns
+ // * (and after the {@code setTarget} that attempts to convey the updated version).
+ // * <p>
+ // * This operation is likely to be expensive and should be used sparingly.
+ // * If possible, it should be buffered for batch processing on sets of call sites.
+ // * <p>
+ // * If {@code sites} contains a null element,
+ // * a {@code NullPointerException} will be raised.
+ // * In this case, some non-null elements in the array may be
+ // * processed before the method returns abnormally.
+ // * Which elements these are (if any) is implementation-dependent.
+ // *
+ // * <h3>Java Memory Model details</h3>
+ // * In terms of the Java Memory Model, this operation performs a synchronization
+ // * action which is comparable in effect to the writing of a volatile variable
+ // * by the current thread, and an eventual volatile read by every other thread
+ // * that may access one of the affected call sites.
+ // * <p>
+ // * The following effects are apparent, for each individual call site {@code S}:
+ // * <ul>
+ // * <li>A new volatile variable {@code V} is created, and written by the current thread.
+ // * As defined by the JMM, this write is a global synchronization event.
+ // * <li>As is normal with thread-local ordering of write events,
+ // * every action already performed by the current thread is
+ // * taken to happen before the volatile write to {@code V}.
+ // * (In some implementations, this means that the current thread
+ // * performs a global release operation.)
+ // * <li>Specifically, the write to the current target of {@code S} is
+ // * taken to happen before the volatile write to {@code V}.
+ // * <li>The volatile write to {@code V} is placed
+ // * (in an implementation specific manner)
+ // * in the global synchronization order.
+ // * <li>Consider an arbitrary thread {@code T} (other than the current thread).
+ // * If {@code T} executes a synchronization action {@code A}
+ // * after the volatile write to {@code V} (in the global synchronization order),
+ // * it is therefore required to see either the current target
+ // * of {@code S}, or a later write to that target,
+ // * if it executes a read on the target of {@code S}.
+ // * (This constraint is called "synchronization-order consistency".)
+ // * <li>The JMM specifically allows optimizing compilers to elide
+ // * reads or writes of variables that are known to be useless.
+ // * Such elided reads and writes have no effect on the happens-before
+ // * relation. Regardless of this fact, the volatile {@code V}
+ // * will not be elided, even though its written value is
+ // * indeterminate and its read value is not used.
+ // * </ul>
+ // * Because of the last point, the implementation behaves as if a
+ // * volatile read of {@code V} were performed by {@code T}
+ // * immediately after its action {@code A}. In the local ordering
+ // * of actions in {@code T}, this read happens before any future
+ // * read of the target of {@code S}. It is as if the
+ // * implementation arbitrarily picked a read of {@code S}'s target
+ // * by {@code T}, and forced a read of {@code V} to precede it,
+ // * thereby ensuring communication of the new target value.
+ // * <p>
+ // * As long as the constraints of the Java Memory Model are obeyed,
+ // * implementations may delay the completion of a {@code syncAll}
+ // * operation while other threads ({@code T} above) continue to
+ // * use previous values of {@code S}'s target.
+ // * However, implementations are (as always) encouraged to avoid
+ // * livelock, and to eventually require all threads to take account
+ // * of the updated target.
+ // *
+ // * <p style="font-size:smaller;">
+ // * <em>Discussion:</em>
+ // * For performance reasons, {@code syncAll} is not a virtual method
+ // * on a single call site, but rather applies to a set of call sites.
+ // * Some implementations may incur a large fixed overhead cost
+ // * for processing one or more synchronization operations,
+ // * but a small incremental cost for each additional call site.
+ // * In any case, this operation is likely to be costly, since
+ // * other threads may have to be somehow interrupted
+ // * in order to make them notice the updated target value.
+ // * However, it may be observed that a single call to synchronize
+ // * several sites has the same formal effect as many calls,
+ // * each on just one of the sites.
+ // *
+ // * <p style="font-size:smaller;">
+ // * <em>Implementation Note:</em>
+ // * Simple implementations of {@code MutableCallSite} may use
+ // * a volatile variable for the target of a mutable call site.
+ // * In such an implementation, the {@code syncAll} method can be a no-op,
+ // * and yet it will conform to the JMM behavior documented above.
+ // *
+ // * @param sites an array of call sites to be synchronized
+ // * @throws NullPointerException if the {@code sites} array reference is null
+ // * or the array contains a null
+ // */
+ // public static void syncAll(MutableCallSite[] sites) {
+ // if (sites.length == 0) return;
+ // STORE_BARRIER.lazySet(0);
+ // for (int i = 0; i < sites.length; i++) {
+ // sites[i].getClass(); // trigger NPE on first null
+ // }
+ // // FIXME: NYI
+ // }
+ // private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/Transformers.java b/ojluni/src/main/java/java/lang/invoke/Transformers.java
index 10ce855..82cc797 100644
--- a/ojluni/src/main/java/java/lang/invoke/Transformers.java
+++ b/ojluni/src/main/java/java/lang/invoke/Transformers.java
@@ -237,27 +237,25 @@
*/
public static class ReferenceArrayElementGetter extends Transformer {
private final Class<?> arrayClass;
- private final StackFrameReader reader;
- private final StackFrameWriter writer;
public ReferenceArrayElementGetter(Class<?> arrayClass) {
super(MethodType.methodType(arrayClass.getComponentType(),
new Class<?>[]{arrayClass, int.class}));
this.arrayClass = arrayClass;
- reader = new StackFrameReader();
- writer = new StackFrameWriter();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(emulatedStackFrame);
- writer.attach(emulatedStackFrame);
// Read the array object and the index from the stack frame.
final Object[] array = (Object[]) reader.nextReference(arrayClass);
final int index = reader.nextInt();
// Write the array element back to the stack frame.
+ final StackFrameWriter writer = new StackFrameWriter();
+ writer.attach(emulatedStackFrame);
writer.makeReturnValueAccessor();
writer.putNextReference(array[index], arrayClass.getComponentType());
}
@@ -268,17 +266,16 @@
*/
public static class ReferenceArrayElementSetter extends Transformer {
private final Class<?> arrayClass;
- private final StackFrameReader reader;
public ReferenceArrayElementSetter(Class<?> arrayClass) {
super(MethodType.methodType(void.class,
new Class<?>[] { arrayClass, int.class, arrayClass.getComponentType() }));
this.arrayClass = arrayClass;
- reader = new StackFrameReader();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(emulatedStackFrame);
// Read the array object, index and the value to write from the stack frame.
@@ -295,23 +292,20 @@
*/
public static class ReferenceIdentity extends Transformer {
private final Class<?> type;
- private final StackFrameReader reader;
- private final StackFrameWriter writer;
public ReferenceIdentity(Class<?> type) {
super(MethodType.methodType(type, type));
this.type = type;
-
- reader = new StackFrameReader();
- writer = new StackFrameWriter();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(emulatedStackFrame);
+
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(emulatedStackFrame);
writer.makeReturnValueAccessor();
-
writer.putNextReference(reader.nextReference(type), type);
}
}
@@ -336,8 +330,6 @@
private char typeChar;
- private final EmulatedStackFrame.StackFrameWriter writer;
-
public Constant(Class<?> type, Object value) {
super(MethodType.methodType(type));
this.type = type;
@@ -372,12 +364,11 @@
} else {
throw new AssertionError("unknown type: " + typeChar);
}
-
- writer = new EmulatedStackFrame.StackFrameWriter();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(emulatedStackFrame);
writer.makeReturnValueAccessor();
@@ -491,9 +482,6 @@
private final MethodHandle target;
private final MethodHandle filter;
- private final EmulatedStackFrame.StackFrameReader returnValueReader;
- private final EmulatedStackFrame.StackFrameWriter filterWriter;
-
private final EmulatedStackFrame.Range allArgs;
public FilterReturnValue(MethodHandle target, MethodHandle filter) {
@@ -502,9 +490,6 @@
this.target = target;
this.filter = filter;
- returnValueReader = new EmulatedStackFrame.StackFrameReader();
- filterWriter = new EmulatedStackFrame.StackFrameWriter();
-
allArgs = EmulatedStackFrame.Range.all(type());
}
@@ -515,15 +500,16 @@
// the same parameter shapes.
EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
emulatedStackFrame.copyRangeTo(targetFrame, allArgs, 0, 0);
-
target.invoke(targetFrame);
// Perform the invoke.
+ final StackFrameReader returnValueReader = new StackFrameReader();
returnValueReader.attach(targetFrame);
returnValueReader.makeReturnValueAccessor();
// Create an emulated frame for the filter and copy all its arguments across.
EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type());
+ final StackFrameWriter filterWriter = new StackFrameWriter();
filterWriter.attach(filterFrame);
final Class<?> returnType = target.type().rtype();
@@ -563,30 +549,24 @@
private final MethodHandle target;
private final int[] reorder;
- private final EmulatedStackFrame.StackFrameWriter writer;
- private final EmulatedStackFrame.StackFrameReader reader;
-
public PermuteArguments(MethodType type, MethodHandle target, int[] reorder) {
super(type);
this.target = target;
this.reorder = reorder;
-
- writer = new EmulatedStackFrame.StackFrameWriter();
- reader = new EmulatedStackFrame.StackFrameReader();
}
@Override
public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(emulatedStackFrame);
- final Class<?>[] ptypes = type().ptypes();
// In the interests of simplicity, we box / unbox arguments while performing
// the permutation. We first iterate through the incoming stack frame and box
// each argument. We then unbox and write out the argument to the target frame
// according to the specified reordering.
Object[] arguments = new Object[reorder.length];
-
+ final Class<?>[] ptypes = type().ptypes();
for (int i = 0; i < ptypes.length; ++i) {
final Class<?> ptype = ptypes[i];
if (!ptype.isPrimitive()) {
@@ -613,6 +593,7 @@
}
EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(target.type());
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(calleeFrame);
for (int i = 0; i < ptypes.length; ++i) {
@@ -1083,7 +1064,6 @@
* arguments that aren't a part of the trailing array.
*/
private final Range copyRange;
- private final StackFrameWriter writer;
Spreader(MethodHandle target, MethodType spreaderType, int numArrayArgs) {
super(spreaderType);
@@ -1102,7 +1082,6 @@
this.numArrayArgs = numArrayArgs;
// Copy all args except for the last argument.
this.copyRange = EmulatedStackFrame.Range.of(spreaderType, 0, arrayOffset);
- this.writer = new StackFrameWriter();
}
@Override
@@ -1115,6 +1094,7 @@
// Attach the writer, prepare to spread the trailing array arguments into
// the callee frame.
+ StackFrameWriter writer = new StackFrameWriter();
writer.attach(targetFrame,
arrayOffset,
copyRange.numReferences,
@@ -1350,9 +1330,6 @@
*/
private final Range copyRange;
- private final StackFrameWriter writer;
- private final StackFrameReader reader;
-
Collector(MethodHandle delegate, Class<?> arrayType, int length) {
super(delegate.type().asCollectorType(arrayType, length));
@@ -1365,8 +1342,6 @@
// Copy all args except for the last argument.
copyRange = EmulatedStackFrame.Range.of(delegate.type(), 0, arrayOffset);
- writer = new StackFrameWriter();
- reader = new StackFrameReader();
}
@Override
@@ -1379,7 +1354,9 @@
// Attach the writer, prepare to spread the trailing array arguments into
// the callee frame.
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(targetFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(callerFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
switch (arrayTypeChar) {
@@ -1481,12 +1458,6 @@
/** The list of filters to apply */
private final MethodHandle[] filters;
- private final StackFrameWriter writer;
- private final StackFrameReader reader;
-
- private final StackFrameWriter filterWriter;
- private final StackFrameReader filterReader;
-
FilterArguments(MethodHandle target, int pos, MethodHandle[] filters) {
super(deriveType(target, pos, filters));
@@ -1494,11 +1465,6 @@
this.pos = pos;
this.filters = filters;
- this.writer = new StackFrameWriter();
- this.reader = new StackFrameReader();
-
- this.filterReader = new StackFrameReader();
- this.filterWriter = new StackFrameWriter();
}
private static MethodType deriveType(MethodHandle target, int pos, MethodHandle[] filters) {
@@ -1512,9 +1478,11 @@
@Override
public void transform(EmulatedStackFrame stackFrame) throws Throwable {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(stackFrame);
EmulatedStackFrame transformedFrame = EmulatedStackFrame.create(target.type());
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(transformedFrame);
final Class<?>[] ptypes = target.type().ptypes();
@@ -1537,12 +1505,14 @@
EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type());
// Copy the next argument from the stack frame to the filter frame.
+ final StackFrameWriter filterWriter = new StackFrameWriter();
filterWriter.attach(filterFrame);
copyNext(reader, filterWriter, filter.type().ptypes()[0]);
filter.invoke(filterFrame);
// Copy the argument back from the filter frame to the stack frame.
+ final StackFrameReader filterReader = new StackFrameReader();
filterReader.attach(filterFrame);
filterReader.makeReturnValueAccessor();
copyNext(filterReader, writer, ptype);
@@ -1581,9 +1551,6 @@
*/
private final Range range2;
- private final StackFrameReader reader;
- private final StackFrameWriter writer;
-
private final int referencesOffset;
private final int stackFrameOffset;
@@ -1606,9 +1573,6 @@
this.range2 = null;
}
- reader = new StackFrameReader();
- writer = new StackFrameWriter();
-
// Calculate the number of primitive bytes (or references) we copy to the
// target frame based on the return value of the combiner.
final Class<?> collectorRType = collector.type().rtype();
@@ -1637,7 +1601,9 @@
// If one of these offsets is not zero, we have a return value to copy.
if (referencesOffset != 0 || stackFrameOffset != 0) {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(filterFrame).makeReturnValueAccessor();
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(targetFrame, pos, range1.numReferences, range1.numBytes);
copyNext(reader, writer, target.type().ptypes()[0]);
}
@@ -1663,9 +1629,6 @@
private final Range combinerArgs;
private final Range targetArgs;
- private final StackFrameReader reader;
- private final StackFrameWriter writer;
-
private final int referencesOffset;
private final int stackFrameOffset;
@@ -1678,9 +1641,6 @@
combinerArgs = Range.all(combiner.type());
targetArgs = Range.all(type());
- reader = new StackFrameReader();
- writer = new StackFrameWriter();
-
final Class<?> combinerRType = combiner.type().rtype();
if (combinerRType == void.class) {
stackFrameOffset = 0;
@@ -1706,7 +1666,9 @@
// If one of these offsets is not zero, we have a return value to copy.
if (referencesOffset != 0 || stackFrameOffset != 0) {
+ final StackFrameReader reader = new StackFrameReader();
reader.attach(combinerFrame).makeReturnValueAccessor();
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(targetFrame);
copyNext(reader, writer, target.type().ptypes()[0]);
}
@@ -1736,7 +1698,6 @@
private final Range range1;
private final Range range2;
- private final StackFrameWriter writer;
InsertArguments(MethodHandle target, int pos, Object[] values) {
super(target.type().dropParameterTypes(pos, pos + values.length));
@@ -1747,8 +1708,6 @@
final MethodType type = type();
range1 = EmulatedStackFrame.Range.of(type, 0, pos);
range2 = Range.of(type, pos, type.parameterCount());
-
- writer = new StackFrameWriter();
}
@Override
@@ -1760,6 +1719,7 @@
// Attach a stack frame writer so that we can copy the next |values.length|
// arguments.
+ final StackFrameWriter writer = new StackFrameWriter();
writer.attach(calleeFrame, pos, range1.numReferences, range1.numBytes);
// Copy all the arguments supplied in |values|.
diff --git a/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java b/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java
new file mode 100644
index 0000000..6444335
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: removed references to MutableCallSite.syncAll().
+/**
+ * A {@code VolatileCallSite} is a {@link CallSite} whose target acts like a volatile variable.
+ * An {@code invokedynamic} instruction linked to a {@code VolatileCallSite} sees updates
+ * to its call site target immediately, even if the update occurs in another thread.
+ * There may be a performance penalty for such tight coupling between threads.
+ * <p>
+ * In other respects, a {@code VolatileCallSite} is interchangeable
+ * with {@code MutableCallSite}.
+ * @see MutableCallSite
+ * @author John Rose, JSR 292 EG
+ */
+public class VolatileCallSite extends CallSite {
+ /**
+ * Creates a call site with a volatile binding to its target.
+ * The initial target is set to a method handle
+ * of the given type which will throw an {@code IllegalStateException} if called.
+ * @param type the method type that this call site will have
+ * @throws NullPointerException if the proposed type is null
+ */
+ public VolatileCallSite(MethodType type) {
+ super(type);
+ }
+
+ /**
+ * Creates a call site with a volatile binding to its target.
+ * The target is set to the given value.
+ * @param target the method handle that will be the initial target of the call site
+ * @throws NullPointerException if the proposed target is null
+ */
+ public VolatileCallSite(MethodHandle target) {
+ super(target);
+ }
+
+ /**
+ * Returns the target method of the call site, which behaves
+ * like a {@code volatile} field of the {@code VolatileCallSite}.
+ * <p>
+ * The interactions of {@code getTarget} with memory are the same
+ * as of a read from a {@code volatile} field.
+ * <p>
+ * In particular, the current thread is required to issue a fresh
+ * read of the target from memory, and must not fail to see
+ * a recent update to the target by another thread.
+ *
+ * @return the linkage state of this call site, a method handle which can change over time
+ * @see #setTarget
+ */
+ @Override public final MethodHandle getTarget() {
+ return getTargetVolatile();
+ }
+
+ /**
+ * Updates the target method of this call site, as a volatile variable.
+ * The type of the new target must agree with the type of the old target.
+ * <p>
+ * The interactions with memory are the same as of a write to a volatile field.
+ * In particular, any threads is guaranteed to see the updated target
+ * the next time it calls {@code getTarget}.
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see #getTarget
+ */
+ @Override public void setTarget(MethodHandle newTarget) {
+ checkTargetChange(getTargetVolatile(), newTarget);
+ setTargetVolatile(newTarget);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return makeDynamicInvoker();
+ }
+}
diff --git a/ojluni/src/main/java/java/lang/ref/Reference.java b/ojluni/src/main/java/java/lang/ref/Reference.java
index 31f37e5..99adb36 100644
--- a/ojluni/src/main/java/java/lang/ref/Reference.java
+++ b/ojluni/src/main/java/java/lang/ref/Reference.java
@@ -26,6 +26,8 @@
package java.lang.ref;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Abstract base class for reference objects. This class defines the
@@ -96,6 +98,7 @@
return getReferent();
}
+ @FastNative
private final native T getReferent();
/**
@@ -111,6 +114,7 @@
// Direct access to the referent is prohibited, clearReferent blocks and set
// the referent to null when it is safe to do so.
+ @FastNative
native void clearReferent();
/* -- Queue operations -- */
diff --git a/ojluni/src/main/java/java/lang/reflect/Array.java b/ojluni/src/main/java/java/lang/reflect/Array.java
index c266060..48863d9 100644
--- a/ojluni/src/main/java/java/lang/reflect/Array.java
+++ b/ojluni/src/main/java/java/lang/reflect/Array.java
@@ -26,6 +26,8 @@
package java.lang.reflect;
+import dalvik.annotation.optimization.FastNative;
+
/**
* The {@code Array} class provides static methods to dynamically create and
* access Java arrays.
@@ -721,6 +723,7 @@
/*
* Create a multi-dimensional array of objects with the specified type.
*/
+ @FastNative
private static native Object createMultiArray(Class<?> componentType, int[] dimensions) throws NegativeArraySizeException;
/**
@@ -760,6 +763,7 @@
/*
* Create a one-dimensional array of objects with the specified type.
*/
+ @FastNative
private static native Object createObjectArray(Class<?> componentType, int length) throws NegativeArraySizeException;
private static IllegalArgumentException notAnArray(Object o) {
diff --git a/ojluni/src/main/java/java/lang/reflect/Constructor.java b/ojluni/src/main/java/java/lang/reflect/Constructor.java
index 7e3a71d..1a99660 100644
--- a/ojluni/src/main/java/java/lang/reflect/Constructor.java
+++ b/ojluni/src/main/java/java/lang/reflect/Constructor.java
@@ -26,6 +26,7 @@
package java.lang.reflect;
+import dalvik.annotation.optimization.FastNative;
import java.lang.annotation.Annotation;
import java.util.Comparator;
@@ -153,6 +154,7 @@
* {@inheritDoc}
*/
@Override
+ @FastNative
public native Class<?>[] getExceptionTypes();
/**
@@ -327,9 +329,11 @@
}
}
+ @FastNative
private static native Object newInstanceFromSerialization(Class<?> ctorClass, Class<?> allocClass)
throws InstantiationException, IllegalArgumentException, InvocationTargetException;
+ @FastNative
private native T newInstance0(Object... args) throws InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException;
diff --git a/ojluni/src/main/java/java/lang/reflect/Executable.java b/ojluni/src/main/java/java/lang/reflect/Executable.java
index 539716f..59f50e1 100644
--- a/ojluni/src/main/java/java/lang/reflect/Executable.java
+++ b/ojluni/src/main/java/java/lang/reflect/Executable.java
@@ -25,6 +25,7 @@
package java.lang.reflect;
+import dalvik.annotation.optimization.FastNative;
import com.android.dex.Dex;
import java.lang.annotation.Annotation;
@@ -420,6 +421,7 @@
private transient volatile boolean hasRealParameterData;
private transient volatile Parameter[] parameters;
+ @FastNative
private native Parameter[] getParameters0();
/**
@@ -534,6 +536,7 @@
// Android-changed: Implemented natively.
return getAnnotationNative(annotationClass);
}
+ @FastNative
private native <T extends Annotation> T getAnnotationNative(Class<T> annotationClass);
/**
@@ -554,6 +557,7 @@
// Android-changed: Implemented natively.
return getDeclaredAnnotationsNative();
}
+ @FastNative
private native Annotation[] getDeclaredAnnotationsNative();
// Android-changed: Additional ART-related fields and logic below that is shared between
@@ -649,6 +653,7 @@
Objects.requireNonNull(annotationType);
return isAnnotationPresentNative(annotationType);
}
+ @FastNative
private native boolean isAnnotationPresentNative(Class<? extends Annotation> annotationType);
final Annotation[][] getParameterAnnotationsInternal() {
@@ -658,6 +663,7 @@
}
return parameterAnnotations;
}
+ @FastNative
private native Annotation[][] getParameterAnnotationsNative();
/**
@@ -721,6 +727,7 @@
}
return result.toString();
}
+ @FastNative
private native String[] getSignatureAnnotation();
final boolean equalNameAndParametersInternal(Method m) {
diff --git a/ojluni/src/main/java/java/lang/reflect/Field.java b/ojluni/src/main/java/java/lang/reflect/Field.java
index 111fa2b..2f1bc31 100644
--- a/ojluni/src/main/java/java/lang/reflect/Field.java
+++ b/ojluni/src/main/java/java/lang/reflect/Field.java
@@ -26,6 +26,7 @@
package java.lang.reflect;
+import dalvik.annotation.optimization.FastNative;
import com.android.dex.Dex;
import java.lang.annotation.Annotation;
@@ -183,6 +184,7 @@
}
return result.toString();
}
+ @FastNative
private native String[] getSignatureAnnotation();
@@ -316,6 +318,7 @@
* @exception ExceptionInInitializerError if the initialization provoked
* by this method fails.
*/
+ @FastNative
public native Object get(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -341,6 +344,7 @@
* by this method fails.
* @see Field#get
*/
+ @FastNative
public native boolean getBoolean(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -366,6 +370,7 @@
* by this method fails.
* @see Field#get
*/
+ @FastNative
public native byte getByte(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -393,6 +398,7 @@
* by this method fails.
* @see Field#get
*/
+ @FastNative
public native char getChar(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -420,6 +426,7 @@
* by this method fails.
* @see Field#get
*/
+ @FastNative
public native short getShort(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -447,6 +454,7 @@
* by this method fails.
* @see Field#get
*/
+ @FastNative
public native int getInt(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -474,6 +482,7 @@
* by this method fails.
* @see Field#get
*/
+ @FastNative
public native long getLong(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -501,6 +510,7 @@
* by this method fails.
* @see Field#get
*/
+ @FastNative
public native float getFloat(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -528,6 +538,7 @@
* by this method fails.
* @see Field#get
*/
+ @FastNative
public native double getDouble(Object obj)
throws IllegalArgumentException, IllegalAccessException;
@@ -597,6 +608,7 @@
* @exception ExceptionInInitializerError if the initialization provoked
* by this method fails.
*/
+ @FastNative
public native void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException;
@@ -624,6 +636,7 @@
* by this method fails.
* @see Field#set
*/
+ @FastNative
public native void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException;
@@ -651,6 +664,7 @@
* by this method fails.
* @see Field#set
*/
+ @FastNative
public native void setByte(Object obj, byte b)
throws IllegalArgumentException, IllegalAccessException;
@@ -678,6 +692,7 @@
* by this method fails.
* @see Field#set
*/
+ @FastNative
public native void setChar(Object obj, char c)
throws IllegalArgumentException, IllegalAccessException;
@@ -705,6 +720,7 @@
* by this method fails.
* @see Field#set
*/
+ @FastNative
public native void setShort(Object obj, short s)
throws IllegalArgumentException, IllegalAccessException;
@@ -732,6 +748,7 @@
* by this method fails.
* @see Field#set
*/
+ @FastNative
public native void setInt(Object obj, int i)
throws IllegalArgumentException, IllegalAccessException;
@@ -759,6 +776,7 @@
* by this method fails.
* @see Field#set
*/
+ @FastNative
public native void setLong(Object obj, long l)
throws IllegalArgumentException, IllegalAccessException;
@@ -786,6 +804,7 @@
* by this method fails.
* @see Field#set
*/
+ @FastNative
public native void setFloat(Object obj, float f)
throws IllegalArgumentException, IllegalAccessException;
@@ -813,6 +832,7 @@
* by this method fails.
* @see Field#set
*/
+ @FastNative
public native void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException;
@@ -825,6 +845,7 @@
Objects.requireNonNull(annotationClass);
return getAnnotationNative(annotationClass);
}
+ @FastNative
private native <A extends Annotation> A getAnnotationNative(Class<A> annotationType);
/**
@@ -845,12 +866,14 @@
}
return isAnnotationPresentNative(annotationType);
}
+ @FastNative
private native boolean isAnnotationPresentNative(Class<? extends Annotation> annotationType);
/**
* {@inheritDoc}
*/
@Override
+ @FastNative
public native Annotation[] getDeclaredAnnotations();
/**
@@ -874,5 +897,6 @@
/**
* @hide - export for use by {@code java.lang.invoke.*}
*/
+ @FastNative
public native long getArtField();
}
diff --git a/ojluni/src/main/java/java/lang/reflect/Method.java b/ojluni/src/main/java/java/lang/reflect/Method.java
index e3fa1c9..4d47edf 100644
--- a/ojluni/src/main/java/java/lang/reflect/Method.java
+++ b/ojluni/src/main/java/java/lang/reflect/Method.java
@@ -26,6 +26,7 @@
package java.lang.reflect;
+import dalvik.annotation.optimization.FastNative;
import java.lang.annotation.Annotation;
import java.util.Comparator;
import libcore.reflect.Types;
@@ -202,6 +203,7 @@
* {@inheritDoc}
*/
@Override
+ @FastNative
public native Class<?>[] getExceptionTypes();
/**
@@ -395,6 +397,7 @@
* @exception ExceptionInInitializerError if the initialization
* provoked by this method fails.
*/
+ @FastNative
public native Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
@@ -461,6 +464,7 @@
* default class value.
* @since 1.5
*/
+ @FastNative
public native Object getDefaultValue();
/**
diff --git a/ojluni/src/main/java/java/lang/reflect/Parameter.java b/ojluni/src/main/java/java/lang/reflect/Parameter.java
index 2d96307..77c26d7 100644
--- a/ojluni/src/main/java/java/lang/reflect/Parameter.java
+++ b/ojluni/src/main/java/java/lang/reflect/Parameter.java
@@ -24,6 +24,7 @@
*/
package java.lang.reflect;
+import dalvik.annotation.optimization.FastNative;
import java.lang.annotation.*;
import java.util.HashMap;
import java.util.Map;
@@ -273,6 +274,7 @@
// Android-changed: Uses native code to obtain annotation information.
return getAnnotationNative(executable, index, annotationClass);
}
+ @FastNative
private static native <A extends Annotation> A getAnnotationNative(
Executable executable, int parameterIndex, Class<A> annotationType);
diff --git a/ojluni/src/main/java/java/lang/reflect/Proxy.java b/ojluni/src/main/java/java/lang/reflect/Proxy.java
index c9e56cf..90a6651 100644
--- a/ojluni/src/main/java/java/lang/reflect/Proxy.java
+++ b/ojluni/src/main/java/java/lang/reflect/Proxy.java
@@ -27,6 +27,7 @@
package java.lang.reflect;
+import dalvik.annotation.optimization.FastNative;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.security.Permission;
@@ -912,6 +913,7 @@
return h.invoke(proxy, method, args);
}
+ @FastNative
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);
diff --git a/ojluni/src/main/java/java/net/URLConnection.java b/ojluni/src/main/java/java/net/URLConnection.java
index 3215f6b..127c289 100644
--- a/ojluni/src/main/java/java/net/URLConnection.java
+++ b/ojluni/src/main/java/java/net/URLConnection.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1511,7 +1511,7 @@
}
if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF) {
- if (c4 == 0xE0) {
+ if (c4 == 0xE0 || c4 == 0xEE) {
return "image/jpeg";
}
@@ -1526,10 +1526,6 @@
c11 == 0)) {
return "image/jpeg";
}
-
- if (c4 == 0xEE) {
- return "image/jpg";
- }
}
if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0 &&
diff --git a/ojluni/src/main/java/java/nio/DirectByteBuffer.java b/ojluni/src/main/java/java/nio/DirectByteBuffer.java
index 7007e15..fc40f26 100644
--- a/ojluni/src/main/java/java/nio/DirectByteBuffer.java
+++ b/ojluni/src/main/java/java/nio/DirectByteBuffer.java
@@ -53,6 +53,7 @@
long allocatedAddress;
final int offset;
boolean isAccessible;
+ boolean isFreed;
MemoryRef(int capacity) {
VMRuntime runtime = VMRuntime.getRuntime();
@@ -61,6 +62,7 @@
// Offset is set to handle the alignment: http://b/16449607
offset = (int) (((allocatedAddress + 7) & ~(long) 7) - allocatedAddress);
isAccessible = true;
+ isFreed = false;
}
MemoryRef(long allocatedAddress) {
@@ -74,6 +76,7 @@
buffer = null;
allocatedAddress = 0;
isAccessible = false;
+ isFreed = true;
}
}
@@ -154,8 +157,8 @@
@Override
public final ByteBuffer duplicate() {
- if (!memoryRef.isAccessible) {
- throw new IllegalStateException("buffer is inaccessible");
+ if (memoryRef.isFreed) {
+ throw new IllegalStateException("buffer has been freed");
}
return new DirectByteBuffer(memoryRef,
this.markValue(),
@@ -168,8 +171,8 @@
@Override
public final ByteBuffer asReadOnlyBuffer() {
- if (!memoryRef.isAccessible) {
- throw new IllegalStateException("buffer is inaccessible");
+ if (memoryRef.isFreed) {
+ throw new IllegalStateException("buffer has been freed");
}
return new DirectByteBuffer(memoryRef,
this.markValue(),
@@ -235,24 +238,24 @@
@Override
public final ByteBuffer put(byte x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
put(ix(nextPutIndex()), x);
return this;
}
@Override
public final ByteBuffer put(int i, byte x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
put(ix(checkIndex(i)), x);
return this;
}
@@ -260,12 +263,12 @@
// This method is not declared final because it is overridden in tests.
@Override
public ByteBuffer put(byte[] src, int srcOffset, int length) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
checkBounds(srcOffset, length, src.length);
int pos = position();
int lim = limit();
@@ -281,12 +284,12 @@
@Override
public final ByteBuffer compact() {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
int pos = position();
int lim = limit();
assert (pos <= lim);
@@ -345,11 +348,17 @@
@Override
char getCharUnchecked(int i) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
return (char) Memory.peekShort(ix(i), !nativeByteOrder);
}
@Override
void getUnchecked(int pos, char[] dst, int dstOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.peekCharArray(ix(pos),
dst, dstOffset, length, !nativeByteOrder);
}
@@ -361,43 +370,49 @@
@Override
public final ByteBuffer putChar(char x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putChar(ix(nextPutIndex(SizeOf.CHAR)), x);
return this;
}
@Override
public final ByteBuffer putChar(int i, char x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putChar(ix(checkIndex(i, SizeOf.CHAR)), x);
return this;
}
@Override
void putCharUnchecked(int i, char x) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
putChar(ix(i), x);
}
@Override
void putUnchecked(int pos, char[] src, int srcOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.pokeCharArray(ix(pos),
src, srcOffset, length, !nativeByteOrder);
}
@Override
public final CharBuffer asCharBuffer() {
- if (!memoryRef.isAccessible) {
- throw new IllegalStateException("buffer is inaccessible");
+ if (memoryRef.isFreed) {
+ throw new IllegalStateException("buffer has been freed");
}
int off = this.position();
int lim = this.limit();
@@ -435,11 +450,17 @@
@Override
short getShortUnchecked(int i) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
return getShort(ix(i));
}
@Override
void getUnchecked(int pos, short[] dst, int dstOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.peekShortArray(ix(pos),
dst, dstOffset, length, !nativeByteOrder);
}
@@ -451,43 +472,49 @@
@Override
public final ByteBuffer putShort(short x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putShort(ix(nextPutIndex(SizeOf.SHORT)), x);
return this;
}
@Override
public final ByteBuffer putShort(int i, short x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putShort(ix(checkIndex(i, SizeOf.SHORT)), x);
return this;
}
@Override
void putShortUnchecked(int i, short x) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
putShort(ix(i), x);
}
@Override
void putUnchecked(int pos, short[] src, int srcOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.pokeShortArray(ix(pos),
src, srcOffset, length, !nativeByteOrder);
}
@Override
public final ShortBuffer asShortBuffer() {
- if (!memoryRef.isAccessible) {
- throw new IllegalStateException("buffer is inaccessible");
+ if (memoryRef.isFreed) {
+ throw new IllegalStateException("buffer has been freed");
}
int off = this.position();
int lim = this.limit();
@@ -525,11 +552,17 @@
@Override
final int getIntUnchecked(int i) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
return getInt(ix(i));
}
@Override
final void getUnchecked(int pos, int[] dst, int dstOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.peekIntArray(ix(pos),
dst, dstOffset, length, !nativeByteOrder);
}
@@ -541,43 +574,49 @@
@Override
public final ByteBuffer putInt(int x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putInt(ix(nextPutIndex(SizeOf.INT)), x);
return this;
}
@Override
public final ByteBuffer putInt(int i, int x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putInt(ix(checkIndex(i, SizeOf.INT)), x);
return this;
}
@Override
final void putIntUnchecked(int i, int x) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
putInt(ix(i), x);
}
@Override
final void putUnchecked(int pos, int[] src, int srcOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.pokeIntArray(ix(pos),
src, srcOffset, length, !nativeByteOrder);
}
@Override
public final IntBuffer asIntBuffer() {
- if (!memoryRef.isAccessible) {
- throw new IllegalStateException("buffer is inaccessible");
+ if (memoryRef.isFreed) {
+ throw new IllegalStateException("buffer has been freed");
}
int off = this.position();
int lim = this.limit();
@@ -615,11 +654,17 @@
@Override
final long getLongUnchecked(int i) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
return getLong(ix(i));
}
@Override
final void getUnchecked(int pos, long[] dst, int dstOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.peekLongArray(ix(pos),
dst, dstOffset, length, !nativeByteOrder);
}
@@ -631,43 +676,49 @@
@Override
public final ByteBuffer putLong(long x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putLong(ix(nextPutIndex(SizeOf.LONG)), x);
return this;
}
@Override
public final ByteBuffer putLong(int i, long x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putLong(ix(checkIndex(i, SizeOf.LONG)), x);
return this;
}
@Override
final void putLongUnchecked(int i, long x) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
putLong(ix(i), x);
}
@Override
final void putUnchecked(int pos, long[] src, int srcOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.pokeLongArray(ix(pos),
src, srcOffset, length, !nativeByteOrder);
}
@Override
public final LongBuffer asLongBuffer() {
- if (!memoryRef.isAccessible) {
- throw new IllegalStateException("buffer is inaccessible");
+ if (memoryRef.isFreed) {
+ throw new IllegalStateException("buffer has been freed");
}
int off = this.position();
int lim = this.limit();
@@ -706,11 +757,17 @@
@Override
final float getFloatUnchecked(int i) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
return getFloat(ix(i));
}
@Override
final void getUnchecked(int pos, float[] dst, int dstOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.peekFloatArray(ix(pos),
dst, dstOffset, length, !nativeByteOrder);
}
@@ -723,43 +780,49 @@
@Override
public final ByteBuffer putFloat(float x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putFloat(ix(nextPutIndex(SizeOf.FLOAT)), x);
return this;
}
@Override
public final ByteBuffer putFloat(int i, float x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putFloat(ix(checkIndex(i, SizeOf.FLOAT)), x);
return this;
}
@Override
final void putFloatUnchecked(int i, float x) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
putFloat(ix(i), x);
}
@Override
final void putUnchecked(int pos, float[] src, int srcOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.pokeFloatArray(ix(pos),
src, srcOffset, length, !nativeByteOrder);
}
@Override
public final FloatBuffer asFloatBuffer() {
- if (!memoryRef.isAccessible) {
- throw new IllegalStateException("buffer is inaccessible");
+ if (memoryRef.isFreed) {
+ throw new IllegalStateException("buffer has been freed");
}
int off = this.position();
int lim = this.limit();
@@ -798,11 +861,17 @@
@Override
final double getDoubleUnchecked(int i) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
return getDouble(ix(i));
}
@Override
final void getUnchecked(int pos, double[] dst, int dstOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.peekDoubleArray(ix(pos),
dst, dstOffset, length, !nativeByteOrder);
}
@@ -815,43 +884,49 @@
@Override
public final ByteBuffer putDouble(double x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putDouble(ix(nextPutIndex(SizeOf.DOUBLE)), x);
return this;
}
@Override
public final ByteBuffer putDouble(int i, double x) {
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (!memoryRef.isAccessible) {
throw new IllegalStateException("buffer is inaccessible");
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
putDouble(ix(checkIndex(i, SizeOf.DOUBLE)), x);
return this;
}
@Override
final void putDoubleUnchecked(int i, double x) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
putDouble(ix(i), x);
}
@Override
final void putUnchecked(int pos, double[] src, int srcOffset, int length) {
+ if (!memoryRef.isAccessible) {
+ throw new IllegalStateException("buffer is inaccessible");
+ }
Memory.pokeDoubleArray(ix(pos),
src, srcOffset, length, !nativeByteOrder);
}
@Override
public final DoubleBuffer asDoubleBuffer() {
- if (!memoryRef.isAccessible) {
- throw new IllegalStateException("buffer is inaccessible");
+ if (memoryRef.isFreed) {
+ throw new IllegalStateException("buffer has been freed");
}
int off = this.position();
int lim = this.limit();
diff --git a/ojluni/src/main/java/java/security/Provider.java b/ojluni/src/main/java/java/security/Provider.java
index deb186c..a932893 100644
--- a/ojluni/src/main/java/java/security/Provider.java
+++ b/ojluni/src/main/java/java/security/Provider.java
@@ -895,15 +895,6 @@
algorithm = algorithm.toUpperCase(ENGLISH);
this.algorithm = intern ? algorithm.intern() : algorithm;
}
- // BEGIN ANDROID-CHANGED
- // The above constructor is not amenable to compile-time execution as Locale.ENGLISH
- // cannot be retrieved. Special-case a constructor for the static field "previousKey".
- private ServiceKey() {
- this.type = "";
- this.originalAlgorithm = "";
- this.algorithm = "";
- }
- // END ANDROID-CHANGED
public int hashCode() {
return type.hashCode() + algorithm.hashCode();
}
@@ -1088,10 +1079,7 @@
// list and queries each provider with the same values until it finds
// a matching service
private static volatile ServiceKey previousKey =
- // BEGIN ANDROID-CHANGED
- // was: new ServiceKey("", "", false);
- new ServiceKey();
- // END ANDROID-CHANGED
+ new ServiceKey("", "", false);
/**
* Get an unmodifiable Set of all services supported by
diff --git a/ojluni/src/main/java/java/text/SimpleDateFormat.java b/ojluni/src/main/java/java/text/SimpleDateFormat.java
index de2a93e..700d5ca 100644
--- a/ojluni/src/main/java/java/text/SimpleDateFormat.java
+++ b/ojluni/src/main/java/java/text/SimpleDateFormat.java
@@ -1821,7 +1821,7 @@
}
if (bestMatch == null) {
// No match found, return error.
- return 0;
+ return -start;
}
}
@@ -1926,7 +1926,7 @@
}
return (start + zoneNames[nameIndex].length());
}
- return 0;
+ return -start;
}
/**
diff --git a/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java b/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
index 676421a..5848e94 100644
--- a/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
+++ b/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
@@ -715,7 +715,7 @@
* only compares the underlying date and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
- * This is equivalent to using {@code date1.toEpochDay() > date2.toEpochDay()}.
+ * This is equivalent to using {@code date1.toEpochDay() > date2.toEpochDay()}.
* <p>
* This default implementation performs the comparison based on the epoch-day.
*
@@ -733,7 +733,7 @@
* only compares the underlying date and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
- * This is equivalent to using {@code date1.toEpochDay() < date2.toEpochDay()}.
+ * This is equivalent to using {@code date1.toEpochDay() < date2.toEpochDay()}.
* <p>
* This default implementation performs the comparison based on the epoch-day.
*
diff --git a/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java b/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
index 15f7d84..29e503e 100644
--- a/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
+++ b/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
@@ -132,7 +132,7 @@
private static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
static Calendar createCalendar() {
- return Calendar.getJapanesImperialInstance(TimeZone.getDefault(), LOCALE);
+ return Calendar.getJapaneseImperialInstance(TimeZone.getDefault(), LOCALE);
}
/**
diff --git a/ojluni/src/main/java/java/time/format/ZoneName.java b/ojluni/src/main/java/java/time/format/ZoneName.java
index 1d58109..c3eb20a 100644
--- a/ojluni/src/main/java/java/time/format/ZoneName.java
+++ b/ojluni/src/main/java/java/time/format/ZoneName.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,12 +28,8 @@
import android.icu.text.TimeZoneNames;
import android.icu.util.TimeZone;
import android.icu.util.ULocale;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
/**
* A helper class to map a zone name to metazone and back to the
diff --git a/ojluni/src/main/java/java/util/Calendar.java b/ojluni/src/main/java/java/util/Calendar.java
index 60c1c97..8593403 100644
--- a/ojluni/src/main/java/java/util/Calendar.java
+++ b/ojluni/src/main/java/java/util/Calendar.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1161,12 +1161,13 @@
return this;
}
+ // Android-changed: fix typo in example code.
/**
* Sets field parameters to their values given by
* {@code fieldValuePairs} that are pairs of a field and its value.
* For example,
* <pre>
- * setFeilds(Calendar.YEAR, 2013,
+ * setFields(Calendar.YEAR, 2013,
* Calendar.MONTH, Calendar.DECEMBER,
* Calendar.DAY_OF_MONTH, 23);</pre>
* is equivalent to the sequence of the following
@@ -1464,12 +1465,17 @@
type = locale.getUnicodeLocaleType("ca");
}
if (type == null) {
+ // Android-changed: don't switch to buddhist calendar based on locale.
+ // See http://b/35138741
+ /*
if (locale.getCountry() == "TH"
- && locale.getLanguage() == "th") {
+ && locale.getLanguage() == "th") {
type = "buddhist";
} else {
type = "gregory";
}
+ */
+ type = "gregory";
}
switch (type) {
case "gregory":
@@ -1483,6 +1489,15 @@
setWeekDefinition(MONDAY, 4);
cal = gcal;
break;
+// Android-changed BEGIN: removed support for "buddhist" and "japanese".
+// case "buddhist":
+// cal = new BuddhistCalendar(zone, locale);
+// cal.clear();
+// break;
+// case "japanese":
+// cal = new JapaneseImperialCalendar(zone, locale, true);
+// break;
+// Android-changed END: removed support for "buddhist" and "japanese".
default:
throw new IllegalArgumentException("unknown calendar type: " + type);
}
@@ -1579,6 +1594,7 @@
*/
protected Calendar(TimeZone zone, Locale aLocale)
{
+ // Android-added BEGIN: Allow aLocale == null
// http://b/16938922.
//
// TODO: This is for backwards compatibility only. Seems like a better idea to throw
@@ -1586,6 +1602,7 @@
if (aLocale == null) {
aLocale = Locale.getDefault();
}
+ // Android-added END: Allow aLocale == null
fields = new int[FIELD_COUNT];
isSet = new boolean[FIELD_COUNT];
stamp = new int[FIELD_COUNT];
@@ -1649,18 +1666,22 @@
return createCalendar(zone, aLocale);
}
+ // Android-added BEGIN: add getJapaneseImperialInstance()
/**
* Create a Japanese Imperial Calendar.
* @hide
*/
- public static Calendar getJapanesImperialInstance(TimeZone zone, Locale aLocale) {
+ public static Calendar getJapaneseImperialInstance(TimeZone zone, Locale aLocale) {
return new JapaneseImperialCalendar(zone, aLocale);
}
+ // Android-added END: add getJapaneseImperialInstance()
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
+ // Android-changed BEGIN: only support GregorianCalendar here
return new GregorianCalendar(zone, aLocale);
+ // Android-changed END: only support GregorianCalendar here
}
/**
@@ -1751,7 +1772,12 @@
public void setTimeInMillis(long millis) {
// If we don't need to recalculate the calendar field values,
// do nothing.
+// Android-changed BEGIN: Removed ZoneInfo support
if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet) {
+// if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet
+// && (zone instanceof ZoneInfo) && !((ZoneInfo)zone).isDirty()) {
+// Android-changed END: Removed ZoneInfo support
+
return;
}
time = millis;
@@ -2034,11 +2060,13 @@
* @since 1.6
*/
public String getDisplayName(int field, int style, Locale locale) {
- // Android-changed: Android has traditionally treated ALL_STYLES as SHORT, even though
+ // Android-changed BEGIN: Treat ALL_STYLES as SHORT
+ // Android has traditionally treated ALL_STYLES as SHORT, even though
// it's not documented to be a valid value for style.
if (style == ALL_STYLES) {
style = SHORT;
}
+ // Android-changed END: Treat ALL_STYLES as SHORT
if (!checkDisplayNameParams(field, style, SHORT, NARROW_FORMAT, locale,
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
@@ -2132,6 +2160,7 @@
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
}
+ // Android-added: Add complete() here to fix leniency, see http://b/35382060
complete();
String calendarType = getCalendarType();
@@ -2179,10 +2208,12 @@
baseStyle < minStyle || baseStyle > maxStyle) {
throw new IllegalArgumentException();
}
- // Android-changed: 3 is not a valid base style (1, 2 and 4 are, though), throw if used.
+ // Android-added BEGIN: Check for invalid baseStyle == 3
+ // 3 is not a valid base style (unlike 1, 2 and 4). Throw if used.
if (baseStyle == 3) {
throw new IllegalArgumentException();
}
+ // Android-added END: Check for invalid baseStyle == 3
if (locale == null) {
throw new NullPointerException();
}
@@ -2595,6 +2626,7 @@
static {
Set<String> set = new HashSet<>(3);
set.add("gregory");
+ // Android-changed: removed "buddhist" and "japanese".
SET = Collections.unmodifiableSet(set);
}
private AvailableCalendarTypes() {
diff --git a/ojluni/src/main/java/java/util/Formatter.java b/ojluni/src/main/java/java/util/Formatter.java
index ca1b990..43d5043 100644
--- a/ojluni/src/main/java/java/util/Formatter.java
+++ b/ojluni/src/main/java/java/util/Formatter.java
@@ -2710,6 +2710,7 @@
if (s != null) {
try {
// Android-changed: FormatSpecifierParser passes in correct String.
+ // index = Integer.parseInt(s.substring(0, s.length() - 1));
index = Integer.parseInt(s);
} catch (NumberFormatException x) {
assert(false);
@@ -2757,6 +2758,8 @@
precision = -1;
if (s != null) {
try {
+ // Android-changed: FormatSpecifierParser passes in correct String.
+ // precision = Integer.parseInt(s.substring(1));
precision = Integer.parseInt(s);
if (precision < 0)
throw new IllegalFormatPrecisionException(precision);
@@ -2789,7 +2792,7 @@
return c;
}
- // Android-changed: FormatSpecifierParser passes in the values instead of a Matcher.
+ // Android-changed BEGIN: FormatSpecifierParser passes in the values instead of a Matcher.
FormatSpecifier(String indexStr, String flagsStr, String widthStr,
String precisionStr, String tTStr, String convStr) {
int idx = 1;
@@ -2806,7 +2809,7 @@
}
conversion(convStr);
-
+ // Android-changed END: FormatSpecifierParser passes in the values instead of a Matcher.
if (dt)
checkDateTime();
else if (Conversion.isGeneral(c))
@@ -2998,6 +3001,7 @@
s = s.substring(0, precision);
if (f.contains(Flags.UPPERCASE)) {
// Android-changed: Use provided locale instead of default, if it is non-null.
+ // s = s.toUpperCase();
s = s.toUpperCase(l != null ? l : Locale.getDefault());
}
a.append(justify(s));
@@ -3369,12 +3373,13 @@
newW = adjustWidth(width - exp.length - 1, f, neg);
localizedMagnitude(sb, mant, f, newW, l);
- // Android-changed: Use localized exponent separator for %e.
+ // Android-changed BEGIN: Use localized exponent separator for %e.
Locale separatorLocale = (l != null) ? l : Locale.getDefault();
LocaleData localeData = LocaleData.get(separatorLocale);
sb.append(f.contains(Flags.UPPERCASE) ?
localeData.exponentSeparator.toUpperCase(separatorLocale) :
localeData.exponentSeparator.toLowerCase(separatorLocale));
+ // Android-changed END: Use localized exponent separator for %e.
Flags flags = f.dup().remove(Flags.GROUP);
char sign = exp[0];
@@ -4455,14 +4460,14 @@
grpSep = dfs.getGroupingSeparator();
DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
grpSize = df.getGroupingSize();
- // Android-changed: http://b/33245708 : Some locales have a group separator but
- // also patterns without groups. If we do not clear the group separator in these
- // cases a divide by zero is thrown when determining where to place the
- // separators.
+ // Android-changed BEGIN: http://b/33245708
+ // Some locales have a group separator but also patterns without groups.
+ // If we do not clear the group separator in these cases a divide by zero
+ // is thrown when determining where to place the separators.
if (!df.isGroupingUsed() || df.getGroupingSize() == 0) {
grpSep = '\0';
}
- // Android-changed: end http://b/33245708.
+ // Android-changed END: http://b/33245708.
}
}
diff --git a/ojluni/src/main/java/java/util/GregorianCalendar.java b/ojluni/src/main/java/java/util/GregorianCalendar.java
index 3d8a7c79..15c7652 100644
--- a/ojluni/src/main/java/java/util/GregorianCalendar.java
+++ b/ojluni/src/main/java/java/util/GregorianCalendar.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -739,10 +739,12 @@
gdate = (BaseCalendar.Date) gcal.newCalendarDate(getZone());
}
+ // Android-added BEGIN
GregorianCalendar(long milliseconds) {
this();
setTimeInMillis(milliseconds);
}
+ // Android-added END
/////////////////
// Public methods
@@ -1078,8 +1080,7 @@
}
fd += delta; // fd is the expected fixed date after the calculation
-
- // Android changed: move time zone related calculation to separate method.
+ // Android-changed BEGIN: time zone related calculation via helper methods
// Calculate the time in the UTC time zone.
long utcTime = (fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
@@ -1092,6 +1093,7 @@
// Update the time and recompute the fields.
setTimeInMillis(millis);
+ // Android-changed END: time zone related calculation via helper methods
}
}
@@ -2344,9 +2346,12 @@
}
if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
if (tz instanceof ZoneInfo) {
- // Android changed: libcore ZoneInfo uses different method to get offsets.
+ // Android changed BEGIN: use libcore.util.ZoneInfo
+ // The method name to get offsets differs from sun.util.calendar.ZoneInfo
+ // zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
ZoneInfo zoneInfo = (ZoneInfo) tz;
zoneOffset = zoneInfo.getOffsetsByUtcTime(time, zoneOffsets);
+ // Android-changed END: use libcore.util.ZoneInfo
} else {
zoneOffset = tz.getOffset(time);
zoneOffsets[0] = tz.getRawOffset();
@@ -2796,11 +2801,11 @@
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
TimeZone zone = getZone();
- // Android changed: move time zone related calculation to separate method.
-
+ // Android-changed BEGIN: time zone related calculation via helper methods
int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
millis = adjustForZoneAndDaylightSavingsTime(tzMask, millis, zone);
+ // Android-changed END: time zone related calculation via helper methods
// Set this calendar's time in milliseconds
time = millis;
@@ -2823,6 +2828,7 @@
setFieldsNormalized(mask);
}
+ // Android-added BEGIN: helper methods for time zone related calculation
/**
* Calculates the time in milliseconds that this calendar represents using the UTC time,
* timezone information (specifically Daylight Savings Time (DST) rules, if any) and knowledge
@@ -2992,6 +2998,7 @@
}
return dstOffset;
}
+ // Android-added END: helper methods for time zone related calculation
/**
* Computes the fixed date under either the Gregorian or the
diff --git a/ojluni/src/main/java/java/util/Hashtable.java b/ojluni/src/main/java/java/util/Hashtable.java
index 9997b94..4f5394e 100644
--- a/ojluni/src/main/java/java/util/Hashtable.java
+++ b/ojluni/src/main/java/java/util/Hashtable.java
@@ -973,10 +973,10 @@
HashtableEntry<Object, Object> entryStack = null;
synchronized (this) {
- // Write out the length, threshold, loadfactor
+ // Write out the threshold and loadFactor
s.defaultWriteObject();
- // Write out length, count of elements
+ // Write out the length and count of elements
s.writeInt(table.length);
s.writeInt(count);
@@ -1006,22 +1006,34 @@
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
- // Read in the length, threshold, and loadfactor
+ // Read in the threshold and loadFactor
s.defaultReadObject();
+ // Validate loadFactor (ignore threshold - it will be re-computed)
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
+ throw new StreamCorruptedException("Illegal Load: " + loadFactor);
+
// Read the original length of the array and number of elements
int origlength = s.readInt();
int elements = s.readInt();
- // Compute new size with a bit of room 5% to grow but
- // no larger than the original size. Make the length
+ // Validate # of elements
+ if (elements < 0)
+ throw new StreamCorruptedException("Illegal # of Elements: " + elements);
+
+ // Clamp original length to be more than elements / loadFactor
+ // (this is the invariant enforced with auto-growth)
+ origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
+
+ // Compute new length with a bit of room 5% + 3 to grow but
+ // no larger than the clamped original length. Make the length
// odd if it's large enough, this helps distribute the entries.
// Guard against the length ending up zero, that's not valid.
- int length = (int)(elements * loadFactor) + (elements / 20) + 3;
+ int length = (int)((elements + elements / 20) / loadFactor) + 3;
+
if (length > elements && (length & 1) == 0)
length--;
- if (origlength > 0 && length > origlength)
- length = origlength;
+ length = Math.min(length, origlength);
table = new HashtableEntry<?,?>[length];
threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
count = 0;
@@ -1032,7 +1044,7 @@
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
- // synch could be eliminated for performance
+ // sync is eliminated for performance
reconstitutionPut(table, key, value);
}
}
@@ -1044,9 +1056,9 @@
*
* <p>This differs from the regular put method in several ways. No
* checking for rehashing is necessary since the number of elements
- * initially in the table is known. The modCount is not incremented
- * because we are creating a new instance. Also, no return value
- * is needed.
+ * initially in the table is known. The modCount is not incremented and
+ * there's no synchronization because we are creating a new instance.
+ * Also, no return value is needed.
*/
private void reconstitutionPut(HashtableEntry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
diff --git a/ojluni/src/main/java/java/util/Locale.java b/ojluni/src/main/java/java/util/Locale.java
index 9c75a32..45949c6 100644
--- a/ojluni/src/main/java/java/util/Locale.java
+++ b/ojluni/src/main/java/java/util/Locale.java
@@ -886,7 +886,8 @@
*/
public static Locale getDefault() {
// do not synchronize this method - see 4071298
- return defaultLocale;
+ // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+ return NoImagePreloadHolder.defaultLocale;
}
/**
@@ -972,6 +973,8 @@
}
private static Locale initDefault(Locale.Category category) {
+ // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+ final Locale defaultLocale = NoImagePreloadHolder.defaultLocale;
return getInstance(
System.getProperty(category.languageKey, defaultLocale.getLanguage()),
System.getProperty(category.scriptKey, defaultLocale.getScript()),
@@ -1012,7 +1015,8 @@
public static synchronized void setDefault(Locale newLocale) {
setDefault(Category.DISPLAY, newLocale);
setDefault(Category.FORMAT, newLocale);
- defaultLocale = newLocale;
+ // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+ NoImagePreloadHolder.defaultLocale = newLocale;
// Android-added: Keep ICU state in sync with java.util.
ICU.setDefaultLocale(newLocale.toLanguageTag());
}
@@ -2184,7 +2188,10 @@
*/
private transient volatile int hashCodeValue = 0;
- private volatile static Locale defaultLocale = initDefault();
+ // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+ private static class NoImagePreloadHolder {
+ public volatile static Locale defaultLocale = initDefault();
+ }
private volatile static Locale defaultDisplayLocale = null;
private volatile static Locale defaultFormatLocale = null;
diff --git a/ojluni/src/main/java/java/util/Spliterator.java b/ojluni/src/main/java/java/util/Spliterator.java
index 18cb082..35322b3 100644
--- a/ojluni/src/main/java/java/util/Spliterator.java
+++ b/ojluni/src/main/java/java/util/Spliterator.java
@@ -125,7 +125,7 @@
* are encountered.
*
* @apiNote
- * <p>Spliterators, like {@code Iterators}s, are for traversing the elements of
+ * <p>Spliterators, like {@code Iterator}s, are for traversing the elements of
* a source. The {@code Spliterator} API was designed to support efficient
* parallel traversal in addition to sequential traversal, by supporting
* decomposition as well as single-element iteration. In addition, the
diff --git a/ojluni/src/main/java/java/util/logging/Level.java b/ojluni/src/main/java/java/util/logging/Level.java
index ce62fd0..afc7035 100644
--- a/ojluni/src/main/java/java/util/logging/Level.java
+++ b/ojluni/src/main/java/java/util/logging/Level.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -602,11 +602,14 @@
if (list != null) {
for (KnownLevel level : list) {
Level other = level.mirroredLevel;
+ Class<? extends Level> type = level.levelObject.getClass();
if (l.value == other.value &&
(l.resourceBundleName == other.resourceBundleName ||
(l.resourceBundleName != null &&
l.resourceBundleName.equals(other.resourceBundleName)))) {
- return level;
+ if (type == l.getClass()) {
+ return level;
+ }
}
}
}
diff --git a/ojluni/src/main/java/java/util/logging/LogRecord.java b/ojluni/src/main/java/java/util/logging/LogRecord.java
index c555ab3..e5ead8b 100644
--- a/ojluni/src/main/java/java/util/logging/LogRecord.java
+++ b/ojluni/src/main/java/java/util/logging/LogRecord.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -501,13 +501,21 @@
throw new IOException("LogRecord: bad version: " + major + "." + minor);
}
int len = in.readInt();
- if (len == -1) {
+ if (len < -1) {
+ throw new NegativeArraySizeException();
+ } else if (len == -1) {
parameters = null;
- } else {
+ } else if (len < 255) {
parameters = new Object[len];
for (int i = 0; i < parameters.length; i++) {
parameters[i] = in.readObject();
}
+ } else {
+ List<Object> params = new ArrayList<>(Math.min(len, 1024));
+ for (int i = 0; i < len; i++) {
+ params.add(in.readObject());
+ }
+ parameters = params.toArray(new Object[params.size()]);
}
// If necessary, try to regenerate the resource bundle.
if (resourceBundleName != null) {
diff --git a/ojluni/src/main/java/java/util/logging/Logger.java b/ojluni/src/main/java/java/util/logging/Logger.java
index c8b76f1..8cf98fe 100644
--- a/ojluni/src/main/java/java/util/logging/Logger.java
+++ b/ojluni/src/main/java/java/util/logging/Logger.java
@@ -805,6 +805,7 @@
* @param level One of the message level identifiers, e.g., SEVERE
* @param msgSupplier A function, which when called, produces the
* desired log message
+ * @since 1.8
*/
public void log(Level level, Supplier<String> msgSupplier) {
if (!isLoggable(level)) {
diff --git a/ojluni/src/main/java/sun/misc/Unsafe.java b/ojluni/src/main/java/sun/misc/Unsafe.java
index 90b9f84..c9ba16e 100644
--- a/ojluni/src/main/java/sun/misc/Unsafe.java
+++ b/ojluni/src/main/java/sun/misc/Unsafe.java
@@ -25,6 +25,7 @@
package sun.misc;
+import dalvik.annotation.optimization.FastNative;
import dalvik.system.VMStack;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -110,7 +111,9 @@
return getArrayIndexScaleForComponentType(component);
}
+ @FastNative
private static native int getArrayBaseOffsetForComponentType(Class component_class);
+ @FastNative
private static native int getArrayIndexScaleForComponentType(Class component_class);
/**
@@ -125,6 +128,7 @@
* @return <code>true</code> if the new value was in fact stored, and
* <code>false</code> if not
*/
+ @FastNative
public native boolean compareAndSwapInt(Object obj, long offset,
int expectedValue, int newValue);
@@ -140,6 +144,7 @@
* @return <code>true</code> if the new value was in fact stored, and
* <code>false</code> if not
*/
+ @FastNative
public native boolean compareAndSwapLong(Object obj, long offset,
long expectedValue, long newValue);
@@ -155,6 +160,7 @@
* @return <code>true</code> if the new value was in fact stored, and
* <code>false</code> if not
*/
+ @FastNative
public native boolean compareAndSwapObject(Object obj, long offset,
Object expectedValue, Object newValue);
@@ -166,6 +172,7 @@
* @param offset offset to the field within <code>obj</code>
* @return the retrieved value
*/
+ @FastNative
public native int getIntVolatile(Object obj, long offset);
/**
@@ -176,6 +183,7 @@
* @param offset offset to the field within <code>obj</code>
* @param newValue the value to store
*/
+ @FastNative
public native void putIntVolatile(Object obj, long offset, int newValue);
/**
@@ -186,6 +194,7 @@
* @param offset offset to the field within <code>obj</code>
* @return the retrieved value
*/
+ @FastNative
public native long getLongVolatile(Object obj, long offset);
/**
@@ -196,6 +205,7 @@
* @param offset offset to the field within <code>obj</code>
* @param newValue the value to store
*/
+ @FastNative
public native void putLongVolatile(Object obj, long offset, long newValue);
/**
@@ -206,6 +216,7 @@
* @param offset offset to the field within <code>obj</code>
* @return the retrieved value
*/
+ @FastNative
public native Object getObjectVolatile(Object obj, long offset);
/**
@@ -216,6 +227,7 @@
* @param offset offset to the field within <code>obj</code>
* @param newValue the value to store
*/
+ @FastNative
public native void putObjectVolatile(Object obj, long offset,
Object newValue);
@@ -226,6 +238,7 @@
* @param offset offset to the field within <code>obj</code>
* @return the retrieved value
*/
+ @FastNative
public native int getInt(Object obj, long offset);
/**
@@ -235,11 +248,13 @@
* @param offset offset to the field within <code>obj</code>
* @param newValue the value to store
*/
+ @FastNative
public native void putInt(Object obj, long offset, int newValue);
/**
* Lazy set an int field.
*/
+ @FastNative
public native void putOrderedInt(Object obj, long offset, int newValue);
/**
@@ -249,6 +264,7 @@
* @param offset offset to the field within <code>obj</code>
* @return the retrieved value
*/
+ @FastNative
public native long getLong(Object obj, long offset);
/**
@@ -258,11 +274,13 @@
* @param offset offset to the field within <code>obj</code>
* @param newValue the value to store
*/
+ @FastNative
public native void putLong(Object obj, long offset, long newValue);
/**
* Lazy set a long field.
*/
+ @FastNative
public native void putOrderedLong(Object obj, long offset, long newValue);
/**
@@ -272,6 +290,7 @@
* @param offset offset to the field within <code>obj</code>
* @return the retrieved value
*/
+ @FastNative
public native Object getObject(Object obj, long offset);
/**
@@ -281,26 +300,40 @@
* @param offset offset to the field within <code>obj</code>
* @param newValue the value to store
*/
+ @FastNative
public native void putObject(Object obj, long offset, Object newValue);
/**
* Lazy set an object field.
*/
+ @FastNative
public native void putOrderedObject(Object obj, long offset,
Object newValue);
+ @FastNative
public native boolean getBoolean(Object obj, long offset);
+ @FastNative
public native void putBoolean(Object obj, long offset, boolean newValue);
+ @FastNative
public native byte getByte(Object obj, long offset);
+ @FastNative
public native void putByte(Object obj, long offset, byte newValue);
+ @FastNative
public native char getChar(Object obj, long offset);
+ @FastNative
public native void putChar(Object obj, long offset, char newValue);
+ @FastNative
public native short getShort(Object obj, long offset);
+ @FastNative
public native void putShort(Object obj, long offset, short newValue);
+ @FastNative
public native float getFloat(Object obj, long offset);
+ @FastNative
public native void putFloat(Object obj, long offset, float newValue);
+ @FastNative
public native double getDouble(Object obj, long offset);
+ @FastNative
public native void putDouble(Object obj, long offset, double newValue);
/**
@@ -348,50 +381,72 @@
*/
public native Object allocateInstance(Class<?> c);
+ @FastNative
public native int addressSize();
+ @FastNative
public native int pageSize();
+ @FastNative
public native long allocateMemory(long bytes);
+ @FastNative
public native void freeMemory(long address);
+ @FastNative
public native void setMemory(long address, long bytes, byte value);
+ @FastNative
public native byte getByte(long address);
+ @FastNative
public native void putByte(long address, byte x);
+ @FastNative
public native short getShort(long address);
+ @FastNative
public native void putShort(long address, short x);
+ @FastNative
public native char getChar(long address);
+ @FastNative
public native void putChar(long address, char x);
+ @FastNative
public native int getInt(long address);
+ @FastNative
public native void putInt(long address, int x);
+ @FastNative
public native long getLong(long address);
+ @FastNative
public native void putLong(long address, long x);
+ @FastNative
public native float getFloat(long address);
+ @FastNative
public native void putFloat(long address, float x);
+ @FastNative
public native double getDouble(long address);
+ @FastNative
public native void putDouble(long address, double x);
+ @FastNative
public native void copyMemoryToPrimitiveArray(long srcAddr,
Object dst, long dstOffset, long bytes);
+ @FastNative
public native void copyMemoryFromPrimitiveArray(Object src, long srcOffset,
long dstAddr, long bytes);
+ @FastNative
public native void copyMemory(long srcAddr, long dstAddr, long bytes);
@@ -512,6 +567,7 @@
* @since 1.8
*/
// @HotSpotIntrinsicCandidate
+ @FastNative
public native void loadFence();
/**
@@ -527,6 +583,7 @@
* @since 1.8
*/
// @HotSpotIntrinsicCandidate
+ @FastNative
public native void storeFence();
/**
@@ -539,5 +596,6 @@
* @since 1.8
*/
// @HotSpotIntrinsicCandidate
+ @FastNative
public native void fullFence();
}
diff --git a/ojluni/src/main/java/sun/nio/ch/DatagramChannelImpl.java b/ojluni/src/main/java/sun/nio/ch/DatagramChannelImpl.java
index c0f8594..08d7375 100644
--- a/ojluni/src/main/java/sun/nio/ch/DatagramChannelImpl.java
+++ b/ojluni/src/main/java/sun/nio/ch/DatagramChannelImpl.java
@@ -1088,7 +1088,10 @@
protected void finalize() throws Throwable {
try {
- guard.warnIfOpen();
+ // Android-changed: Add CloseGuard support.
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
// fd is null if constructor threw exception
if (fd != null)
close();
diff --git a/ojluni/src/main/native/Character.cpp b/ojluni/src/main/native/Character.cpp
index faa27b8..6a82fde 100644
--- a/ojluni/src/main/native/Character.cpp
+++ b/ojluni/src/main/native/Character.cpp
@@ -21,14 +21,12 @@
#include "jni.h"
#include "jvm.h"
#include "JNIHelp.h"
+#include "nativehelper/jni_macros.h"
#include "unicode/uchar.h"
#include "unicode/uscript.h"
#include <math.h>
#include <stdio.h> // For BUFSIZ
-#define NATIVE_METHOD(className, functionName, signature) \
-{ #functionName, signature, (void*)(className ## _ ## functionName) }
-
JNIEXPORT jboolean JNICALL
Character_isLowerCaseImpl(JNIEnv* env, jclass, jint codePoint) {
return u_islower(codePoint);
@@ -161,29 +159,29 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Character, digitImpl, "!(II)I"),
- NATIVE_METHOD(Character, getDirectionalityImpl, "!(I)B"),
+ FAST_NATIVE_METHOD(Character, digitImpl, "(II)I"),
+ FAST_NATIVE_METHOD(Character, getDirectionalityImpl, "(I)B"),
NATIVE_METHOD(Character, getNameImpl, "(I)Ljava/lang/String;"),
- NATIVE_METHOD(Character, getNumericValueImpl, "!(I)I"),
- NATIVE_METHOD(Character, getTypeImpl, "!(I)I"),
- NATIVE_METHOD(Character, isAlphabeticImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isDefinedImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isDigitImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isIdentifierIgnorableImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isIdeographicImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isLetterImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isLetterOrDigitImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isLowerCaseImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isMirroredImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isSpaceCharImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isTitleCaseImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isUnicodeIdentifierPartImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isUnicodeIdentifierStartImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isUpperCaseImpl, "!(I)Z"),
- NATIVE_METHOD(Character, isWhitespaceImpl, "!(I)Z"),
- NATIVE_METHOD(Character, toLowerCaseImpl, "!(I)I"),
- NATIVE_METHOD(Character, toTitleCaseImpl, "!(I)I"),
- NATIVE_METHOD(Character, toUpperCaseImpl, "!(I)I"),
+ FAST_NATIVE_METHOD(Character, getNumericValueImpl, "(I)I"),
+ FAST_NATIVE_METHOD(Character, getTypeImpl, "(I)I"),
+ FAST_NATIVE_METHOD(Character, isAlphabeticImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isDefinedImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isDigitImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isIdentifierIgnorableImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isIdeographicImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isLetterImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isLetterOrDigitImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isLowerCaseImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isMirroredImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isSpaceCharImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isTitleCaseImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isUnicodeIdentifierPartImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isUnicodeIdentifierStartImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isUpperCaseImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, isWhitespaceImpl, "(I)Z"),
+ FAST_NATIVE_METHOD(Character, toLowerCaseImpl, "(I)I"),
+ FAST_NATIVE_METHOD(Character, toTitleCaseImpl, "(I)I"),
+ FAST_NATIVE_METHOD(Character, toUpperCaseImpl, "(I)I"),
};
void register_java_lang_Character(JNIEnv* env) {
diff --git a/ojluni/src/main/native/Math.c b/ojluni/src/main/native/Math.c
index 7bc076c..c5482ba 100644
--- a/ojluni/src/main/native/Math.c
+++ b/ojluni/src/main/native/Math.c
@@ -20,13 +20,11 @@
#include "jni.h"
#include "JNIHelp.h"
+#include "nativehelper/jni_macros.h"
#include <stdlib.h>
#include <math.h>
-#define NATIVE_METHOD(className, functionName, signature) \
-{ #functionName, signature, (void*)(className ## _ ## functionName) }
-
JNIEXPORT jdouble JNICALL
Math_cos(JNIEnv *env, jclass unused, jdouble d) {
return cos(d);
@@ -145,29 +143,29 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Math, IEEEremainder, "!(DD)D"),
- NATIVE_METHOD(Math, acos, "!(D)D"),
- NATIVE_METHOD(Math, asin, "!(D)D"),
- NATIVE_METHOD(Math, atan, "!(D)D"),
- NATIVE_METHOD(Math, atan2, "!(DD)D"),
- NATIVE_METHOD(Math, cbrt, "!(D)D"),
- NATIVE_METHOD(Math, cos, "!(D)D"),
- NATIVE_METHOD(Math, ceil, "!(D)D"),
- NATIVE_METHOD(Math, cosh, "!(D)D"),
- NATIVE_METHOD(Math, exp, "!(D)D"),
- NATIVE_METHOD(Math, expm1, "!(D)D"),
- NATIVE_METHOD(Math, floor, "!(D)D"),
- NATIVE_METHOD(Math, hypot, "!(DD)D"),
- NATIVE_METHOD(Math, log, "!(D)D"),
- NATIVE_METHOD(Math, log10, "!(D)D"),
- NATIVE_METHOD(Math, log1p, "!(D)D"),
- NATIVE_METHOD(Math, pow, "!(DD)D"),
- NATIVE_METHOD(Math, rint, "!(D)D"),
- NATIVE_METHOD(Math, sin, "!(D)D"),
- NATIVE_METHOD(Math, sinh, "!(D)D"),
- NATIVE_METHOD(Math, sqrt, "!(D)D"),
- NATIVE_METHOD(Math, tan, "!(D)D"),
- NATIVE_METHOD(Math, tanh, "!(D)D"),
+ FAST_NATIVE_METHOD(Math, IEEEremainder, "(DD)D"),
+ FAST_NATIVE_METHOD(Math, acos, "(D)D"),
+ FAST_NATIVE_METHOD(Math, asin, "(D)D"),
+ FAST_NATIVE_METHOD(Math, atan, "(D)D"),
+ FAST_NATIVE_METHOD(Math, atan2, "(DD)D"),
+ FAST_NATIVE_METHOD(Math, cbrt, "(D)D"),
+ FAST_NATIVE_METHOD(Math, cos, "(D)D"),
+ FAST_NATIVE_METHOD(Math, ceil, "(D)D"),
+ FAST_NATIVE_METHOD(Math, cosh, "(D)D"),
+ FAST_NATIVE_METHOD(Math, exp, "(D)D"),
+ FAST_NATIVE_METHOD(Math, expm1, "(D)D"),
+ FAST_NATIVE_METHOD(Math, floor, "(D)D"),
+ FAST_NATIVE_METHOD(Math, hypot, "(DD)D"),
+ FAST_NATIVE_METHOD(Math, log, "(D)D"),
+ FAST_NATIVE_METHOD(Math, log10, "(D)D"),
+ FAST_NATIVE_METHOD(Math, log1p, "(D)D"),
+ FAST_NATIVE_METHOD(Math, pow, "(DD)D"),
+ FAST_NATIVE_METHOD(Math, rint, "(D)D"),
+ FAST_NATIVE_METHOD(Math, sin, "(D)D"),
+ FAST_NATIVE_METHOD(Math, sinh, "(D)D"),
+ FAST_NATIVE_METHOD(Math, sqrt, "(D)D"),
+ FAST_NATIVE_METHOD(Math, tan, "(D)D"),
+ FAST_NATIVE_METHOD(Math, tanh, "(D)D"),
};
void register_java_lang_Math(JNIEnv* env) {
diff --git a/ojluni/src/main/native/Runtime.c b/ojluni/src/main/native/Runtime.c
index 90a8898..61de356 100644
--- a/ojluni/src/main/native/Runtime.c
+++ b/ojluni/src/main/native/Runtime.c
@@ -41,8 +41,7 @@
#include "JNIHelp.h"
-#define NATIVE_METHOD(className, functionName, signature) \
-{ #functionName, signature, (void*)(className ## _ ## functionName) }
+#include "nativehelper/jni_macros.h"
JNIEXPORT jlong JNICALL
Runtime_freeMemory(JNIEnv *env, jobject this)
@@ -82,9 +81,9 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Runtime, freeMemory, "!()J"),
- NATIVE_METHOD(Runtime, totalMemory, "!()J"),
- NATIVE_METHOD(Runtime, maxMemory, "!()J"),
+ FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
+ FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
+ FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
NATIVE_METHOD(Runtime, gc, "()V"),
NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
NATIVE_METHOD(Runtime, nativeLoad,
diff --git a/ojluni/src/main/native/openjdksub.mk b/ojluni/src/main/native/openjdksub.mk
index 9e7dc07..3d37d00 100644
--- a/ojluni/src/main/native/openjdksub.mk
+++ b/ojluni/src/main/native/openjdksub.mk
@@ -70,6 +70,5 @@
LOCAL_C_INCLUDES += \
libcore/$(srcdir) \
external/fdlibm \
- external/openssl/include \
external/zlib \
external/icu/icu4c/source/common \
diff --git a/ojluni/src/test/java/nio/file/TestUtil.java b/ojluni/src/test/java/nio/file/TestUtil.java
new file mode 100644
index 0000000..2d102fd
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/TestUtil.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+// Android-changed: Adapted from jdk/test/java/nio/file/TestUtil.java
+// Android-changed: Added package & made methods public
+package test.java.nio.file;
+
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Random;
+import java.io.IOException;
+
+public class TestUtil {
+ private TestUtil() {
+ }
+
+ static public Path createTemporaryDirectory(String where) throws IOException {
+ Path dir = FileSystems.getDefault().getPath(where);
+ return Files.createTempDirectory(dir, "name");
+ }
+
+ static public Path createTemporaryDirectory() throws IOException {
+ return Files.createTempDirectory("name");
+ }
+
+ static public void removeAll(Path dir) throws IOException {
+ Files.walkFileTree(dir, new FileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ try {
+ Files.delete(file);
+ } catch (IOException x) {
+ System.err.format("Unable to delete %s: %s\n", file, x);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+ try {
+ Files.delete(dir);
+ } catch (IOException x) {
+ System.err.format("Unable to delete %s: %s\n", dir, x);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) {
+ System.err.format("Unable to visit %s: %s\n", file, exc);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ static public void deleteUnchecked(Path file) {
+ try {
+ Files.delete(file);
+ } catch (IOException exc) {
+ System.err.format("Unable to delete %s: %s\n", file, exc);
+ }
+ }
+
+ /**
+ * Creates a directory tree in the given directory so that the total
+ * size of the path is more than 2k in size. This is used for long
+ * path tests on Windows.
+ */
+ static public Path createDirectoryWithLongPath(Path dir)
+ throws IOException
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i=0; i<240; i++) {
+ sb.append('A');
+ }
+ String name = sb.toString();
+ do {
+ dir = dir.resolve(name).resolve(".");
+ Files.createDirectory(dir);
+ } while (dir.toString().length() < 2048);
+ return dir;
+ }
+
+ /**
+ * Returns true if symbolic links are supported
+ */
+ static public boolean supportsLinks(Path dir) {
+ Path link = dir.resolve("testlink");
+ Path target = dir.resolve("testtarget");
+ try {
+ Files.createSymbolicLink(link, target);
+ Files.delete(link);
+ return true;
+ } catch (UnsupportedOperationException x) {
+ return false;
+ } catch (IOException x) {
+ return false;
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java b/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java
new file mode 100644
index 0000000..7f0c17b
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 7076310
+ * @summary Test AclEntry.Builder setFlags and setPermissions with empty sets
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/AclEntry/EmptySet.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+
+import java.nio.file.attribute.*;
+import java.util.*;
+
+/*
+ * Test for bug 7076310 "(file) AclEntry.Builder setFlags throws
+ * IllegalArgumentException if set argument is empty"
+ * The bug is only applies when the given Set is NOT an instance of EnumSet.
+ *
+ * The setPermissions method also has the same problem.
+ */
+// Android-changed: Renamed from "EmptySet"
+public class AclEntryEmptySetTest {
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() {
+ Set<AclEntryFlag> flags = new HashSet<>();
+ AclEntry.newBuilder().setFlags(flags);
+
+ Set<AclEntryPermission> perms = new HashSet<>();
+ AclEntry.newBuilder().setPermissions(perms);
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java
new file mode 100644
index 0000000..2d9e32c
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333 6891404
+ * @summary Unit test for java.nio.file.attribute.AclFileAttribueView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/AclFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+import static java.nio.file.attribute.AclEntryType.*;
+import static java.nio.file.attribute.AclEntryPermission.*;
+import static java.nio.file.attribute.AclEntryFlag.*;
+
+// Android-changed: Renamed from "Basic"
+public class AclFileAttributeViewTest {
+
+ static void printAcl(List<AclEntry> acl) {
+ for (AclEntry entry: acl) {
+ System.out.format(" %s%n", entry);
+ }
+ }
+
+ // sanity check read and writing ACL
+ static void testReadWrite(Path dir) throws IOException {
+ Path file = dir.resolve("foo");
+ if (Files.notExists(file))
+ Files.createFile(file);
+
+ AclFileAttributeView view =
+ Files.getFileAttributeView(file, AclFileAttributeView.class);
+
+ // print existing ACL
+ List<AclEntry> acl = view.getAcl();
+ System.out.println(" -- current ACL --");
+ printAcl(acl);
+
+ // insert entry to grant owner read access
+ UserPrincipal owner = view.getOwner();
+ AclEntry entry = AclEntry.newBuilder()
+ .setType(ALLOW)
+ .setPrincipal(owner)
+ .setPermissions(READ_DATA, READ_ATTRIBUTES)
+ .build();
+ System.out.println(" -- insert (entry 0) --");
+ System.out.format(" %s%n", entry);
+ acl.add(0, entry);
+ view.setAcl(acl);
+
+ // re-ACL and check entry
+ List<AclEntry> newacl = view.getAcl();
+ System.out.println(" -- current ACL --");
+ printAcl(acl);
+ if (!newacl.get(0).equals(entry)) {
+ throw new RuntimeException("Entry 0 is not expected");
+ }
+
+ // if PosixFileAttributeView then repeat test with OWNER@
+ if (Files.getFileStore(file).supportsFileAttributeView("posix")) {
+ owner = file.getFileSystem().getUserPrincipalLookupService()
+ .lookupPrincipalByName("OWNER@");
+ entry = AclEntry.newBuilder(entry).setPrincipal(owner).build();
+
+ System.out.println(" -- replace (entry 0) --");
+ System.out.format(" %s%n", entry);
+
+ acl.set(0, entry);
+ view.setAcl(acl);
+ newacl = view.getAcl();
+ System.out.println(" -- current ACL --");
+ printAcl(acl);
+ if (!newacl.get(0).equals(entry)) {
+ throw new RuntimeException("Entry 0 is not expected");
+ }
+ }
+ }
+
+ static FileAttribute<List<AclEntry>> asAclAttribute(final List<AclEntry> acl) {
+ return new FileAttribute<List<AclEntry>>() {
+ public String name() { return "acl:acl"; }
+ public List<AclEntry> value() { return acl; }
+ };
+ }
+
+ static void assertEquals(List<AclEntry> actual, List<AclEntry> expected) {
+ if (!actual.equals(expected)) {
+ System.err.format("Actual: %s\n", actual);
+ System.err.format("Expected: %s\n", expected);
+ throw new RuntimeException("ACL not expected");
+ }
+ }
+
+ // sanity check create a file or directory with initial ACL
+ static void testCreateFile(Path dir) throws IOException {
+ UserPrincipal user = Files.getOwner(dir);
+ AclFileAttributeView view;
+
+ // create file with initial ACL
+ System.out.println("-- create file with initial ACL --");
+ Path file = dir.resolve("gus");
+ List<AclEntry> fileAcl = Arrays.asList(
+ AclEntry.newBuilder()
+ .setType(AclEntryType.ALLOW)
+ .setPrincipal(user)
+ .setPermissions(SYNCHRONIZE, READ_DATA, WRITE_DATA,
+ READ_ATTRIBUTES, READ_ACL, WRITE_ATTRIBUTES, DELETE)
+ .build());
+ Files.createFile(file, asAclAttribute(fileAcl));
+ view = Files.getFileAttributeView(file, AclFileAttributeView.class);
+ assertEquals(view.getAcl(), fileAcl);
+
+ // create directory with initial ACL
+ System.out.println("-- create directory with initial ACL --");
+ Path subdir = dir.resolve("stuff");
+ List<AclEntry> dirAcl = Arrays.asList(
+ AclEntry.newBuilder()
+ .setType(AclEntryType.ALLOW)
+ .setPrincipal(user)
+ .setPermissions(SYNCHRONIZE, ADD_FILE, DELETE)
+ .build(),
+ AclEntry.newBuilder(fileAcl.get(0))
+ .setFlags(FILE_INHERIT)
+ .build());
+ Files.createDirectory(subdir, asAclAttribute(dirAcl));
+ view = Files.getFileAttributeView(subdir, AclFileAttributeView.class);
+ assertEquals(view.getAcl(), dirAcl);
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // use work directory rather than system temporary directory to
+ // improve chances that ACLs are supported
+ // Android-changed: Switched to temp dir due to permissions
+ // Path dir = Paths.get("./work" + new Random().nextInt());
+ // Files.createTempDirectory(dir);
+ Path dir = Files.createTempDirectory("acl");
+ try {
+ if (!Files.getFileStore(dir).supportsFileAttributeView("acl")) {
+ System.out.println("ACLs not supported - test skipped!");
+ return;
+ }
+ testReadWrite(dir);
+
+ // only currently feasible on Windows
+ if (System.getProperty("os.name").startsWith("Windows"))
+ testCreateFile(dir);
+
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
new file mode 100644
index 0000000..b393981
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8011536
+ * @summary Basic test for creationTime attribute on platforms/file systems
+ * that support it.
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.Path;
+import java.nio.file.Files;
+import java.nio.file.attribute.*;
+import java.time.Instant;
+import java.io.IOException;
+
+// Android-changed: Renamed from "CreationTime"
+public class BasicFileAttributeViewCreationTimeTest {
+
+ private static final java.io.PrintStream err = System.err;
+
+ /**
+ * Reads the creationTime attribute
+ */
+ private static FileTime creationTime(Path file) throws IOException {
+ return Files.readAttributes(file, BasicFileAttributes.class).creationTime();
+ }
+
+ /**
+ * Sets the creationTime attribute
+ */
+ private static void setCreationTime(Path file, FileTime time) throws IOException {
+ BasicFileAttributeView view =
+ Files.getFileAttributeView(file, BasicFileAttributeView.class);
+ view.setTimes(null, null, time);
+ }
+
+ static void test(Path top) throws IOException {
+ Path file = Files.createFile(top.resolve("foo"));
+
+ /**
+ * Check that creationTime reported
+ */
+ FileTime creationTime = creationTime(file);
+ Instant now = Instant.now();
+ if (Math.abs(creationTime.toMillis()-now.toEpochMilli()) > 10000L) {
+ err.println("File creation time reported as: " + creationTime);
+ throw new RuntimeException("Expected to be close to: " + now);
+ }
+
+ /**
+ * Is the creationTime attribute supported here?
+ */
+ boolean supportsCreationTimeRead = false;
+ boolean supportsCreationTimeWrite = false;
+ String os = System.getProperty("os.name");
+ if (os.contains("OS X") && Files.getFileStore(file).type().equals("hfs")) {
+ supportsCreationTimeRead = true;
+ } else if (os.startsWith("Windows")) {
+ String type = Files.getFileStore(file).type();
+ if (type.equals("NTFS") || type.equals("FAT")) {
+ supportsCreationTimeRead = true;
+ supportsCreationTimeWrite = true;
+ }
+ }
+
+ /**
+ * If the creation-time attribute is supported then change the file's
+ * last modified and check that it doesn't change the creation-time.
+ */
+ if (supportsCreationTimeRead) {
+ // change modified time by +1 hour
+ Instant plusHour = Instant.now().plusSeconds(60L * 60L);
+ Files.setLastModifiedTime(file, FileTime.from(plusHour));
+ FileTime current = creationTime(file);
+ if (!current.equals(creationTime))
+ throw new RuntimeException("Creation time should not have changed");
+ }
+
+ /**
+ * If the creation-time attribute is supported and can be changed then
+ * check that the change is effective.
+ */
+ if (supportsCreationTimeWrite) {
+ // change creation time by -1 hour
+ Instant minusHour = Instant.now().minusSeconds(60L * 60L);
+ creationTime = FileTime.from(minusHour);
+ setCreationTime(file, creationTime);
+ FileTime current = creationTime(file);
+ if (Math.abs(creationTime.toMillis()-current.toMillis()) > 1000L)
+ throw new RuntimeException("Creation time not changed");
+ }
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // create temporary directory to run tests
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ test(dir);
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java
new file mode 100644
index 0000000..82f4e0e
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.BasicFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.io.*;
+
+// Android-changed: Renamed from "Basic"
+public class BasicFileAttributeViewTest {
+
+ static void check(boolean okay, String msg) {
+ if (!okay)
+ throw new RuntimeException(msg);
+ }
+
+ static void checkAttributesOfDirectory(Path dir)
+ throws IOException
+ {
+ BasicFileAttributes attrs = Files.readAttributes(dir, BasicFileAttributes.class);
+ check(attrs.isDirectory(), "is a directory");
+ check(!attrs.isRegularFile(), "is not a regular file");
+ check(!attrs.isSymbolicLink(), "is not a link");
+ check(!attrs.isOther(), "is not other");
+
+ // last-modified-time should match java.io.File in seconds
+ File f = new File(dir.toString());
+ check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS),
+ "last-modified time should be the same");
+ }
+
+ static void checkAttributesOfFile(Path dir, Path file)
+ throws IOException
+ {
+ BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
+ check(attrs.isRegularFile(), "is a regular file");
+ check(!attrs.isDirectory(), "is not a directory");
+ check(!attrs.isSymbolicLink(), "is not a link");
+ check(!attrs.isOther(), "is not other");
+
+ // size and last-modified-time should match java.io.File in seconds
+ File f = new File(file.toString());
+ check(f.length() == attrs.size(), "size should be the same");
+ check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS),
+ "last-modified time should be the same");
+
+ // copy last-modified time from directory to file,
+ // re-read attribtues, and check they match
+ BasicFileAttributeView view =
+ Files.getFileAttributeView(file, BasicFileAttributeView.class);
+ BasicFileAttributes dirAttrs = Files.readAttributes(dir, BasicFileAttributes.class);
+ view.setTimes(dirAttrs.lastModifiedTime(), null, null);
+
+ attrs = view.readAttributes();
+ check(attrs.lastModifiedTime().equals(dirAttrs.lastModifiedTime()),
+ "last-modified time should be equal");
+
+ // security tests
+ check (!(attrs instanceof PosixFileAttributes),
+ "should not be able to cast to PosixFileAttributes");
+ }
+
+ static void checkAttributesOfLink(Path link)
+ throws IOException
+ {
+ BasicFileAttributes attrs =
+ Files.readAttributes(link, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+ check(attrs.isSymbolicLink(), "is a link");
+ check(!attrs.isDirectory(), "is a directory");
+ check(!attrs.isRegularFile(), "is not a regular file");
+ check(!attrs.isOther(), "is not other");
+ }
+
+ static void attributeReadWriteTests(Path dir)
+ throws IOException
+ {
+ // create file
+ Path file = dir.resolve("foo");
+ try (OutputStream out = Files.newOutputStream(file)) {
+ out.write("this is not an empty file".getBytes("UTF-8"));
+ }
+
+ // check attributes of directory and file
+ checkAttributesOfDirectory(dir);
+ checkAttributesOfFile(dir, file);
+
+ // symbolic links may be supported
+ Path link = dir.resolve("link");
+ try {
+ Files.createSymbolicLink(link, file);
+ } catch (UnsupportedOperationException x) {
+ return;
+ } catch (IOException x) {
+ return;
+ }
+ checkAttributesOfLink(link);
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // create temporary directory to run tests
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ attributeReadWriteTests(dir);
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java
new file mode 100644
index 0000000..8386a31
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.DosFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/DosFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+
+// Android-changed: Renamed from "Basic"
+public class DosFileAttributeViewTest {
+
+ static void check(boolean okay) {
+ if (!okay)
+ throw new RuntimeException("Test failed");
+ }
+
+ // exercise each setter/getter method, leaving all attributes unset
+ static void testAttributes(DosFileAttributeView view) throws IOException {
+ view.setReadOnly(true);
+ check(view.readAttributes().isReadOnly());
+ view.setReadOnly(false);
+ check(!view.readAttributes().isReadOnly());
+ view.setHidden(true);
+ check(view.readAttributes().isHidden());
+ view.setHidden(false);
+ check(!view.readAttributes().isHidden());
+ view.setArchive(true);
+ check(view.readAttributes().isArchive());
+ view.setArchive(false);
+ check(!view.readAttributes().isArchive());
+ view.setSystem(true);
+ check(view.readAttributes().isSystem());
+ view.setSystem(false);
+ check(!view.readAttributes().isSystem());
+ }
+
+ // set the value of all attributes
+ static void setAll(DosFileAttributeView view, boolean value)
+ throws IOException
+ {
+ view.setReadOnly(value);
+ view.setHidden(value);
+ view.setArchive(value);
+ view.setSystem(value);
+ }
+
+ // read and write FAT attributes
+ static void readWriteTests(Path dir) throws IOException {
+
+ // create "foo" and test that we can read/write each FAT attribute
+ Path file = Files.createFile(dir.resolve("foo"));
+ try {
+ testAttributes(Files.getFileAttributeView(file, DosFileAttributeView.class));
+
+ // Following tests use a symbolic link so skip if not supported
+ if (!TestUtil.supportsLinks(dir))
+ return;
+
+ Path link = dir.resolve("link");
+ Files.createSymbolicLink(link, file);
+
+ // test following links
+ testAttributes(Files.getFileAttributeView(link, DosFileAttributeView.class));
+
+ // test not following links
+ try {
+ try {
+ testAttributes(Files
+ .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS));
+ } catch (IOException x) {
+ // access to link attributes not supported
+ return;
+ }
+
+ // set all attributes on link
+ // run test on target of link (which leaves them all un-set)
+ // check that attributes of link remain all set
+ setAll(Files
+ .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS), true);
+ testAttributes(Files
+ .getFileAttributeView(link, DosFileAttributeView.class));
+ DosFileAttributes attrs =
+ Files.getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS)
+ .readAttributes();
+ check(attrs.isReadOnly());
+ check(attrs.isHidden());
+ check(attrs.isArchive());
+ check(attrs.isSystem());
+ setAll(Files
+ .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS), false);
+
+ // set all attributes on target
+ // run test on link (which leaves them all un-set)
+ // check that attributes of target remain all set
+ setAll(Files.getFileAttributeView(link, DosFileAttributeView.class), true);
+ testAttributes(Files
+ .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS));
+ attrs = Files.getFileAttributeView(link, DosFileAttributeView.class).readAttributes();
+ check(attrs.isReadOnly());
+ check(attrs.isHidden());
+ check(attrs.isArchive());
+ check(attrs.isSystem());
+ setAll(Files.getFileAttributeView(link, DosFileAttributeView.class), false);
+ } finally {
+ TestUtil.deleteUnchecked(link);
+ }
+ } finally {
+ TestUtil.deleteUnchecked(file);
+ }
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // create temporary directory to run tests
+ Path dir = TestUtil.createTemporaryDirectory();
+
+ try {
+ // skip test if DOS file attributes not supported
+ if (!Files.getFileStore(dir).supportsFileAttributeView("dos")) {
+ System.out.println("DOS file attribute not supported.");
+ return;
+ }
+ readWriteTests(dir);
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java b/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java
new file mode 100644
index 0000000..43c5d61
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 6844313 8011647
+ * @summary Unit test for java.nio.file.FileTime
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/FileTime/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
+import static java.util.concurrent.TimeUnit.*;
+import java.util.Random;
+import java.util.EnumSet;
+
+// Android-changed: Renamed from "Basic"
+public class FileTimeTest {
+
+ static final Random rand = new Random();
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() {
+ long now = System.currentTimeMillis();
+ long tomorrowInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) + 1;
+ long yesterdayInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) - 1;
+
+ Instant nowInstant = Instant.ofEpochMilli(now);
+
+ // equals
+ eq(now, MILLISECONDS, now, MILLISECONDS);
+ eq(now, MILLISECONDS, now*1000L, MICROSECONDS);
+ neq(now, MILLISECONDS, 0, MILLISECONDS);
+ neq(now, MILLISECONDS, 0, MICROSECONDS);
+
+ eq(nowInstant, now, MILLISECONDS);
+ eq(nowInstant, now*1000L, MICROSECONDS);
+ neq(nowInstant, 0, MILLISECONDS);
+ neq(nowInstant, 0, MICROSECONDS);
+
+ // compareTo
+ cmp(now, MILLISECONDS, now, MILLISECONDS, 0);
+ cmp(now, MILLISECONDS, now*1000L, MICROSECONDS, 0);
+ cmp(now, MILLISECONDS, now-1234, MILLISECONDS, 1);
+ cmp(now, MILLISECONDS, now+1234, MILLISECONDS, -1);
+
+ cmp(tomorrowInDays, DAYS, now, MILLISECONDS, 1);
+ cmp(now, MILLISECONDS, tomorrowInDays, DAYS, -1);
+ cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1);
+ cmp(now, MILLISECONDS, yesterdayInDays, DAYS, 1);
+ cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1);
+
+ cmp(Long.MAX_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, 1);
+ cmp(Long.MAX_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, 1);
+ cmp(Long.MIN_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, -1);
+ cmp(Long.MIN_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, -1);
+
+ cmp(Instant.MIN, Long.MIN_VALUE, DAYS, 1);
+ cmp(Instant.MIN, Long.MIN_VALUE, HOURS, 1);
+ cmp(Instant.MIN, Long.MIN_VALUE, MINUTES, 1);
+ cmp(Instant.MIN, Long.MIN_VALUE, SECONDS, 1);
+ cmp(Instant.MIN, Instant.MIN.getEpochSecond() - 1, SECONDS, 1);
+ cmp(Instant.MIN, Instant.MIN.getEpochSecond() - 100, SECONDS, 1);
+ cmp(Instant.MIN, Instant.MIN.getEpochSecond(), SECONDS, 0);
+
+ cmp(Instant.MAX, Long.MAX_VALUE, DAYS, -1);
+ cmp(Instant.MAX, Long.MAX_VALUE, HOURS, -1);
+ cmp(Instant.MAX, Long.MAX_VALUE, MINUTES, -1);
+ cmp(Instant.MAX, Long.MAX_VALUE, SECONDS, -1);
+ cmp(Instant.MAX, Instant.MAX.getEpochSecond() + 1, SECONDS, -1);
+ cmp(Instant.MAX, Instant.MAX.getEpochSecond() + 100, SECONDS, -1);
+ cmp(Instant.MAX, Instant.MAX.getEpochSecond(), SECONDS, 0);
+
+ cmp(nowInstant, now, MILLISECONDS, 0);
+ cmp(nowInstant, now*1000L, MICROSECONDS, 0);
+ cmp(nowInstant, now-1234, MILLISECONDS, 1);
+ cmp(nowInstant, now+1234, MILLISECONDS, -1);
+ cmp(nowInstant, tomorrowInDays, DAYS, -1);
+ cmp(nowInstant, yesterdayInDays, DAYS, 1);
+
+ // to(TimeUnit)
+ to(MILLISECONDS.convert(1, DAYS) - 1, MILLISECONDS);
+ to(MILLISECONDS.convert(1, DAYS) + 0, MILLISECONDS);
+ to(MILLISECONDS.convert(1, DAYS) + 1, MILLISECONDS);
+ to(1, MILLISECONDS);
+ to(0, MILLISECONDS);
+ to(1, MILLISECONDS);
+ to(MILLISECONDS.convert(-1, DAYS) - 1, MILLISECONDS);
+ to(MILLISECONDS.convert(-1, DAYS) + 0, MILLISECONDS);
+ to(MILLISECONDS.convert(-1, DAYS) + 1, MILLISECONDS);
+ for (TimeUnit unit: TimeUnit.values()) {
+ for (int i=0; i<100; i++) { to(rand.nextLong(), unit); }
+ to(Long.MIN_VALUE, unit);
+ to(Long.MAX_VALUE, unit);
+ }
+
+ // toInstant()
+ int N = 1000;
+ for (TimeUnit unit : EnumSet.allOf(TimeUnit.class)) {
+ for (int i = 0; i < N; i++) {
+ long value = rand.nextLong();
+ FileTime ft = FileTime.from(value, unit);
+ Instant instant = ft.toInstant();
+ if (instant != Instant.MIN && instant != Instant.MAX) {
+ eqTime(value, unit, instant);
+ }
+ }
+ }
+ for (TimeUnit unit : EnumSet.allOf(TimeUnit.class)) {
+ long value = Long.MIN_VALUE;
+ FileTime ft = FileTime.from(value, unit);
+ Instant instant = ft.toInstant();
+ if (unit.compareTo(TimeUnit.SECONDS) < 0) {
+ eqTime(value, unit, instant);
+ } else if (!instant.equals(Instant.MIN)) {
+ throw new RuntimeException("should overflow to MIN");
+ }
+ value = Long.MAX_VALUE;
+ ft = FileTime.from(value, unit);
+ instant = ft.toInstant();
+ if (unit.compareTo(TimeUnit.SECONDS) < 0) {
+ eqTime(value, unit, instant);
+ } else if (!instant.equals(Instant.MAX)) {
+ throw new RuntimeException("should overflow to MAX");
+ }
+ }
+
+ // from(Instant)
+ final long MAX_SECOND = 31556889864403199L;
+ for (int i = 0; i < N; i++) {
+ long v = rand.nextLong();
+ long secs = v % MAX_SECOND;
+ Instant instant = Instant.ofEpochSecond(secs, rand.nextInt(1000_000_000));
+ FileTime ft = FileTime.from(instant);
+ if (!ft.toInstant().equals(instant) || ft.to(SECONDS) != secs) {
+ throw new RuntimeException("from(Instant) failed");
+ }
+ long millis = v;
+ instant = Instant.ofEpochMilli(millis);
+ ft = FileTime.from(instant);
+ if (!ft.toInstant().equals(instant) ||
+ ft.toMillis() != instant.toEpochMilli()) {
+ throw new RuntimeException("from(Instant) failed");
+ }
+ long nanos = v;
+ ft = FileTime.from(nanos, NANOSECONDS);
+ secs = nanos / 1000_000_000;
+ nanos = nanos % 1000_000_000;
+ instant = Instant.ofEpochSecond(secs, nanos);
+ if (!ft.equals(FileTime.from(instant))) {
+ throw new RuntimeException("from(Instant) failed");
+ }
+ }
+
+ // toString
+ ts(1L, DAYS, "1970-01-02T00:00:00Z");
+ ts(1L, HOURS, "1970-01-01T01:00:00Z");
+ ts(1L, MINUTES, "1970-01-01T00:01:00Z");
+ ts(1L, SECONDS, "1970-01-01T00:00:01Z");
+ ts(1L, MILLISECONDS, "1970-01-01T00:00:00.001Z");
+ ts(1L, MICROSECONDS, "1970-01-01T00:00:00.000001Z");
+ ts(1L, NANOSECONDS, "1970-01-01T00:00:00.000000001Z");
+ ts(999999999L, NANOSECONDS, "1970-01-01T00:00:00.999999999Z");
+ ts(9999999999L, NANOSECONDS, "1970-01-01T00:00:09.999999999Z");
+
+ ts(-1L, DAYS, "1969-12-31T00:00:00Z");
+ ts(-1L, HOURS, "1969-12-31T23:00:00Z");
+ ts(-1L, MINUTES, "1969-12-31T23:59:00Z");
+ ts(-1L, SECONDS, "1969-12-31T23:59:59Z");
+ ts(-1L, MILLISECONDS, "1969-12-31T23:59:59.999Z");
+ ts(-1L, MICROSECONDS, "1969-12-31T23:59:59.999999Z");
+ ts(-1L, NANOSECONDS, "1969-12-31T23:59:59.999999999Z");
+ ts(-999999999L, NANOSECONDS, "1969-12-31T23:59:59.000000001Z");
+ ts(-9999999999L, NANOSECONDS, "1969-12-31T23:59:50.000000001Z");
+
+ ts(-62135596799999L, MILLISECONDS, "0001-01-01T00:00:00.001Z");
+ ts(-62135596800000L, MILLISECONDS, "0001-01-01T00:00:00Z");
+ ts(-62135596800001L, MILLISECONDS, "-0001-12-31T23:59:59.999Z");
+
+ ts(253402300799999L, MILLISECONDS, "9999-12-31T23:59:59.999Z");
+ ts(-377642044800001L, MILLISECONDS, "-9999-12-31T23:59:59.999Z");
+
+ // NTFS epoch in usec.
+ ts(-11644473600000000L, MICROSECONDS, "1601-01-01T00:00:00Z");
+
+ ts(Instant.MIN, "-1000000001-01-01T00:00:00Z");
+ ts(Instant.MAX, "1000000000-12-31T23:59:59.999999999Z");
+
+ try {
+ FileTime.from(0L, null);
+ throw new RuntimeException("NullPointerException expected");
+ } catch (NullPointerException npe) { }
+ try {
+ FileTime.from(null);
+ throw new RuntimeException("NullPointerException expected");
+ } catch (NullPointerException npe) { }
+
+ FileTime time = FileTime.fromMillis(now);
+ if (time.equals(null))
+ throw new RuntimeException("should not be equal to null");
+ try {
+ time.compareTo(null);
+ throw new RuntimeException("NullPointerException expected");
+ } catch (NullPointerException npe) { }
+
+ // Instant + toMilli() overflow
+ overflow(Long.MAX_VALUE,
+ FileTime.from(Instant.MAX).toMillis());
+ overflow(Long.MAX_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1))
+ .toMillis());
+ overflow(Long.MIN_VALUE,
+ FileTime.from(Instant.MIN).toMillis());
+ overflow(Long.MIN_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1))
+ .toMillis());
+
+ // Instant + to(TimeUnit) overflow
+ overflow(Long.MAX_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1))
+ .to(MILLISECONDS));
+ overflow(Long.MAX_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000,
+ MILLISECONDS.toNanos(1000)))
+ .to(MILLISECONDS));
+ overflow(Long.MIN_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1))
+ .to(MILLISECONDS));
+ overflow(Long.MIN_VALUE,
+ FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000,
+ -MILLISECONDS.toNanos(1)))
+ .to(MILLISECONDS));
+ }
+
+ static void overflow(long minmax, long v) {
+ if (v != minmax)
+ throw new RuntimeException("saturates to Long.MIN/MAX_VALUE expected");
+ }
+
+ static void cmp(long v1, TimeUnit u1, long v2, TimeUnit u2, int expected) {
+ int result = FileTime.from(v1, u1).compareTo(FileTime.from(v2, u2));
+ if (result != expected)
+ throw new RuntimeException("unexpected order");
+ }
+
+ static void cmp(Instant ins, long v2, TimeUnit u2, int expected) {
+ int result = FileTime.from(ins).compareTo(FileTime.from(v2, u2));
+ if (result != expected)
+ throw new RuntimeException("unexpected order");
+ }
+
+ static void eq(long v1, TimeUnit u1, long v2, TimeUnit u2) {
+ FileTime t1 = FileTime.from(v1, u1);
+ FileTime t2 = FileTime.from(v2, u2);
+ if (!t1.equals(t2))
+ throw new RuntimeException("not equal");
+ if (t1.hashCode() != t2.hashCode())
+ throw new RuntimeException("hashCodes should be equal");
+ }
+
+ static void eq(Instant ins, long v2, TimeUnit u2) {
+ FileTime t1 = FileTime.from(ins);
+ FileTime t2 = FileTime.from(v2, u2);
+ if (!t1.equals(t2))
+ throw new RuntimeException("not equal");
+ if (t1.hashCode() != t2.hashCode())
+ throw new RuntimeException("hashCodes should be equal");
+ }
+
+ static void eqTime(long value, TimeUnit unit, Instant instant) {
+ long secs = SECONDS.convert(value, unit);
+ long nanos = NANOSECONDS.convert(value - unit.convert(secs, SECONDS), unit);
+ if (nanos < 0) { // normalize nanoOfSecond to positive
+ secs -= 1;
+ nanos += 1000_000_000;
+ }
+ if (secs != instant.getEpochSecond() || (int)nanos != instant.getNano()) {
+ System.err.println(" ins=" + instant);
+ throw new RuntimeException("ft and instant are not the same time point");
+ }
+ }
+
+ static void neq(long v1, TimeUnit u1, long v2, TimeUnit u2) {
+ FileTime t1 = FileTime.from(v1, u1);
+ FileTime t2 = FileTime.from(v2, u2);
+ if (t1.equals(t2))
+ throw new RuntimeException("should not be equal");
+ }
+
+ static void neq(Instant ins, long v2, TimeUnit u2) {
+ FileTime t1 = FileTime.from(ins);
+ FileTime t2 = FileTime.from(v2, u2);
+ if (t1.equals(t2))
+ throw new RuntimeException("should not be equal");
+ }
+
+ static void to(long v, TimeUnit unit) {
+ FileTime t = FileTime.from(v, unit);
+ for (TimeUnit u: TimeUnit.values()) {
+ long result = t.to(u);
+ long expected = u.convert(v, unit);
+ if (result != expected) {
+ throw new RuntimeException("unexpected result");
+ }
+ }
+ }
+
+ static void ts(long v, TimeUnit unit, String expected) {
+ String result = FileTime.from(v, unit).toString();
+ if (!result.equals(expected)) {
+ System.err.format("FileTime.from(%d, %s).toString() failed\n", v, unit);
+ System.err.format("Expected: %s\n", expected);
+ System.err.format(" Got: %s\n", result);
+ throw new RuntimeException();
+ }
+ }
+
+ static void ts(Instant instant, String expected) {
+ String result = FileTime.from(instant).toString();
+ if (!result.equals(expected)) {
+ System.err.format("FileTime.from(%s).toString() failed\n", instant);
+ System.err.format("Expected: %s\n", expected);
+ System.err.format(" Got: %s\n", result);
+ throw new RuntimeException();
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
new file mode 100644
index 0000000..7825837
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.PosixFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/PosixFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Unit test for PosixFileAttributeView, passing silently if this attribute
+ * view is not available.
+ */
+
+// Android-changed: Renamed from "Basic"
+public class PosixFileAttributeViewTest {
+
+ /**
+ * Use view to update permission to the given mode and check that the
+ * permissions have been updated.
+ */
+ static void testPermissions(Path file, String mode) throws IOException {
+ System.out.format("change mode: %s\n", mode);
+ Set<PosixFilePermission> perms = PosixFilePermissions.fromString(mode);
+
+ // change permissions and re-read them.
+ Files.setPosixFilePermissions(file, perms);
+ Set<PosixFilePermission> current = Files.getPosixFilePermissions(file);
+ if (!current.equals(perms)) {
+ throw new RuntimeException("Actual permissions: " +
+ PosixFilePermissions.toString(current) + ", expected: " +
+ PosixFilePermissions.toString(perms));
+ }
+
+ // repeat test using setAttribute/getAttribute
+ Files.setAttribute(file, "posix:permissions", perms);
+ current = (Set<PosixFilePermission>)Files.getAttribute(file, "posix:permissions");
+ if (!current.equals(perms)) {
+ throw new RuntimeException("Actual permissions: " +
+ PosixFilePermissions.toString(current) + ", expected: " +
+ PosixFilePermissions.toString(perms));
+ }
+ }
+
+ /**
+ * Check that the actual permissions of a file match or make it more
+ * secure than requested
+ */
+ static void checkSecure(Set<PosixFilePermission> requested,
+ Set<PosixFilePermission> actual)
+ {
+ for (PosixFilePermission perm: actual) {
+ if (!requested.contains(perm)) {
+ throw new RuntimeException("Actual permissions: " +
+ PosixFilePermissions.toString(actual) + ", requested: " +
+ PosixFilePermissions.toString(requested) +
+ " - file is less secure than requested");
+ }
+ }
+ }
+
+ /**
+ * Create file with given mode and check that the file is created with a
+ * mode that is not less secure
+ */
+ static void createWithPermissions(Path file,
+ String mode)
+ throws IOException
+ {
+ Set<PosixFilePermission> requested = PosixFilePermissions.fromString(mode);
+ FileAttribute<Set<PosixFilePermission>> attr =
+ PosixFilePermissions.asFileAttribute(requested);
+ System.out.format("create file with mode: %s\n", mode);
+ Files.createFile(file, attr);
+ try {
+ checkSecure(requested,
+ Files.getFileAttributeView(file, PosixFileAttributeView.class)
+ .readAttributes()
+ .permissions());
+ } finally {
+ Files.delete(file);
+ }
+
+ System.out.format("create directory with mode: %s\n", mode);
+ Files.createDirectory(file, attr);
+ try {
+ checkSecure(requested,
+ Files.getFileAttributeView(file, PosixFileAttributeView.class)
+ .readAttributes()
+ .permissions());
+ } finally {
+ Files.delete(file);
+ }
+ }
+
+ /**
+ * Test the setPermissions/permissions methods.
+ */
+ static void permissionTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Permission Tests --");
+
+ // create file and test updating and reading its permissions
+ Path file = dir.resolve("foo");
+ System.out.format("create %s\n", file);
+ Files.createFile(file);
+ try {
+ // get initial permissions so that we can restore them later
+ PosixFileAttributeView view =
+ Files.getFileAttributeView(file, PosixFileAttributeView.class);
+ Set<PosixFilePermission> save = view.readAttributes()
+ .permissions();
+
+ // test various modes
+ try {
+ testPermissions(file, "---------");
+ testPermissions(file, "r--------");
+ testPermissions(file, "-w-------");
+ testPermissions(file, "--x------");
+ testPermissions(file, "rwx------");
+ testPermissions(file, "---r-----");
+ testPermissions(file, "----w----");
+ testPermissions(file, "-----x---");
+ testPermissions(file, "---rwx---");
+ testPermissions(file, "------r--");
+ testPermissions(file, "-------w-");
+ testPermissions(file, "--------x");
+ testPermissions(file, "------rwx");
+ testPermissions(file, "r--r-----");
+ testPermissions(file, "r--r--r--");
+ testPermissions(file, "rw-rw----");
+ testPermissions(file, "rwxrwx---");
+ testPermissions(file, "rw-rw-r--");
+ testPermissions(file, "r-xr-x---");
+ testPermissions(file, "r-xr-xr-x");
+ testPermissions(file, "rwxrwxrwx");
+ } finally {
+ view.setPermissions(save);
+ }
+ } finally {
+ Files.delete(file);
+ }
+
+ // create link (to file that doesn't exist) and test reading of
+ // permissions
+ if (TestUtil.supportsLinks(dir)) {
+ Path link = dir.resolve("link");
+ System.out.format("create link %s\n", link);
+ Files.createSymbolicLink(link, file);
+ try {
+ PosixFileAttributes attrs =
+ Files.getFileAttributeView(link,
+ PosixFileAttributeView.class,
+ NOFOLLOW_LINKS)
+ .readAttributes();
+ if (!attrs.isSymbolicLink()) {
+ throw new RuntimeException("not a link");
+ }
+ } finally {
+ Files.delete(link);
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Test creating a file and directory with initial permissios
+ */
+ static void createTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Create Tests --");
+
+ Path file = dir.resolve("foo");
+
+ createWithPermissions(file, "---------");
+ createWithPermissions(file, "r--------");
+ createWithPermissions(file, "-w-------");
+ createWithPermissions(file, "--x------");
+ createWithPermissions(file, "rwx------");
+ createWithPermissions(file, "---r-----");
+ createWithPermissions(file, "----w----");
+ createWithPermissions(file, "-----x---");
+ createWithPermissions(file, "---rwx---");
+ createWithPermissions(file, "------r--");
+ createWithPermissions(file, "-------w-");
+ createWithPermissions(file, "--------x");
+ createWithPermissions(file, "------rwx");
+ createWithPermissions(file, "r--r-----");
+ createWithPermissions(file, "r--r--r--");
+ createWithPermissions(file, "rw-rw----");
+ createWithPermissions(file, "rwxrwx---");
+ createWithPermissions(file, "rw-rw-r--");
+ createWithPermissions(file, "r-xr-x---");
+ createWithPermissions(file, "r-xr-xr-x");
+ createWithPermissions(file, "rwxrwxrwx");
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Test setOwner/setGroup methods - this test simply exercises the
+ * methods to avoid configuration.
+ */
+ static void ownerTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Owner Tests --");
+
+ Path file = dir.resolve("gus");
+ System.out.format("create %s\n", file);
+
+ Files.createFile(file);
+ try {
+
+ // read attributes of directory to get owner/group
+ PosixFileAttributeView view =
+ Files.getFileAttributeView(file, PosixFileAttributeView.class);
+ PosixFileAttributes attrs = view.readAttributes();
+
+ // set to existing owner/group
+ view.setOwner(attrs.owner());
+ view.setGroup(attrs.group());
+
+ // repeat test using set/getAttribute
+ UserPrincipal owner = (UserPrincipal)Files.getAttribute(file, "posix:owner");
+ Files.setAttribute(file, "posix:owner", owner);
+ UserPrincipal group = (UserPrincipal)Files.getAttribute(file, "posix:group");
+ Files.setAttribute(file, "posix:group", group);
+
+ } finally {
+ Files.delete(file);
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Test the lookupPrincipalByName/lookupPrincipalByGroupName methods
+ */
+ static void lookupPrincipalTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Lookup UserPrincipal Tests --");
+
+ UserPrincipalLookupService lookupService = dir.getFileSystem()
+ .getUserPrincipalLookupService();
+
+ // read attributes of directory to get owner/group
+ PosixFileAttributes attrs = Files.readAttributes(dir, PosixFileAttributes.class);
+
+ // lookup owner and check it matches file's owner
+ System.out.format("lookup: %s\n", attrs.owner().getName());
+ try {
+ UserPrincipal owner = lookupService.lookupPrincipalByName(attrs.owner().getName());
+ if (owner instanceof GroupPrincipal)
+ throw new RuntimeException("owner is a group?");
+ if (!owner.equals(attrs.owner()))
+ throw new RuntimeException("owner different from file owner");
+ } catch (UserPrincipalNotFoundException x) {
+ System.out.println("user not found - test skipped");
+ }
+
+ // lookup group and check it matches file's group-owner
+ System.out.format("lookup group: %s\n", attrs.group().getName());
+ try {
+ GroupPrincipal group = lookupService.lookupPrincipalByGroupName(attrs.group().getName());
+ if (!group.equals(attrs.group()))
+ throw new RuntimeException("group different from file group-owner");
+ } catch (UserPrincipalNotFoundException x) {
+ System.out.println("group not found - test skipped");
+ }
+
+ // test that UserPrincipalNotFoundException is thrown
+ String invalidPrincipal = "scumbag99";
+ try {
+ System.out.format("lookup: %s\n", invalidPrincipal);
+ lookupService.lookupPrincipalByName(invalidPrincipal);
+ throw new RuntimeException("'" + invalidPrincipal + "' is a valid user?");
+ } catch (UserPrincipalNotFoundException x) {
+ }
+ try {
+ System.out.format("lookup group: %s\n", invalidPrincipal);
+ lookupService.lookupPrincipalByGroupName("idonotexist");
+ throw new RuntimeException("'" + invalidPrincipal + "' is a valid group?");
+ } catch (UserPrincipalNotFoundException x) {
+ }
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Test various exceptions are thrown as expected
+ */
+ @SuppressWarnings("unchecked")
+ static void exceptionsTests(Path dir)
+ throws IOException
+ {
+ System.out.println("-- Exceptions --");
+
+ PosixFileAttributeView view =
+ Files.getFileAttributeView(dir,PosixFileAttributeView.class);
+
+ // NullPointerException
+ try {
+ view.setOwner(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+ try {
+ view.setGroup(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+
+ UserPrincipalLookupService lookupService = dir.getFileSystem()
+ .getUserPrincipalLookupService();
+ try {
+ lookupService.lookupPrincipalByName(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+ try {
+ lookupService.lookupPrincipalByGroupName(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+ try {
+ view.setPermissions(null);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+ try {
+ Set<PosixFilePermission> perms = new HashSet<>();
+ perms.add(null);
+ view.setPermissions(perms);
+ throw new RuntimeException("NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ }
+
+ // ClassCastException
+ try {
+ Set perms = new HashSet(); // raw type
+ perms.add(new Object());
+ view.setPermissions(perms);
+ throw new RuntimeException("ClassCastException not thrown");
+ } catch (ClassCastException x) {
+ }
+
+ System.out.println("OKAY");
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ if (!Files.getFileStore(dir).supportsFileAttributeView("posix")) {
+ System.out.println("PosixFileAttributeView not supported");
+ return;
+ }
+
+ permissionTests(dir);
+ createTests(dir);
+ ownerTests(dir);
+ lookupPrincipalTests(dir);
+ exceptionsTests(dir);
+
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java
new file mode 100644
index 0000000..6fb3c78
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.UserDefinedFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Random;
+import java.io.IOException;
+
+// Android-changed: Renamed from "Basic"
+public class UserDefinedFileAttributeViewTest {
+
+ private static Random rand = new Random();
+
+ private static final String ATTR_NAME = "mime_type";
+ private static final String ATTR_VALUE = "text/plain";
+ private static final String ATTR_VALUE2 = "text/html";
+
+ static interface Task {
+ void run() throws Exception;
+ }
+
+ static void tryCatch(Class<? extends Throwable> ex, Task task) {
+ boolean caught = false;
+ try {
+ task.run();
+ } catch (Throwable x) {
+ if (ex.isAssignableFrom(x.getClass())) {
+ caught = true;
+ } else {
+ throw new RuntimeException(x);
+ }
+ }
+ if (!caught)
+ throw new RuntimeException(ex.getName() + " expected");
+ }
+
+ static void expectNullPointerException(Task task) {
+ tryCatch(NullPointerException.class, task);
+ }
+
+ static boolean hasAttribute(UserDefinedFileAttributeView view, String attr)
+ throws IOException
+ {
+ for (String name: view.list()) {
+ if (name.equals(ATTR_NAME))
+ return true;
+ }
+ return false;
+ }
+
+ static void test(Path file, LinkOption... options) throws IOException {
+ final UserDefinedFileAttributeView view =
+ Files.getFileAttributeView(file, UserDefinedFileAttributeView.class, options);
+ ByteBuffer buf = rand.nextBoolean() ?
+ ByteBuffer.allocate(100) : ByteBuffer.allocateDirect(100);
+
+ // Test: write
+ buf.put(ATTR_VALUE.getBytes()).flip();
+ int size = buf.remaining();
+ int nwrote = view.write(ATTR_NAME, buf);
+ if (nwrote != size)
+ throw new RuntimeException("Unexpected number of bytes written");
+
+ // Test: size
+ if (view.size(ATTR_NAME) != size)
+ throw new RuntimeException("Unexpected size");
+
+ // Test: read
+ buf.clear();
+ int nread = view.read(ATTR_NAME, buf);
+ if (nread != size)
+ throw new RuntimeException("Unexpected number of bytes read");
+ buf.flip();
+ String value = Charset.defaultCharset().decode(buf).toString();
+ if (!value.equals(ATTR_VALUE))
+ throw new RuntimeException("Unexpected attribute value");
+
+ // Test: read with insufficient space
+ tryCatch(IOException.class, new Task() {
+ public void run() throws IOException {
+ view.read(ATTR_NAME, ByteBuffer.allocateDirect(1));
+ }});
+
+ // Test: replace value
+ buf.clear();
+ buf.put(ATTR_VALUE2.getBytes()).flip();
+ size = buf.remaining();
+ view.write(ATTR_NAME, buf);
+ if (view.size(ATTR_NAME) != size)
+ throw new RuntimeException("Unexpected size");
+
+ // Test: list
+ if (!hasAttribute(view, ATTR_NAME))
+ throw new RuntimeException("Attribute name not in list");
+
+ // Test: delete
+ view.delete(ATTR_NAME);
+ if (hasAttribute(view, ATTR_NAME))
+ throw new RuntimeException("Attribute name in list");
+
+ // Test: dynamic access
+ String name = "user:" + ATTR_NAME;
+ byte[] valueAsBytes = ATTR_VALUE.getBytes();
+ Files.setAttribute(file, name, valueAsBytes);
+ byte[] actualAsBytes = (byte[])Files.getAttribute(file, name);
+ if (!Arrays.equals(valueAsBytes, actualAsBytes))
+ throw new RuntimeException("Unexpected attribute value");
+ Map<String,?> map = Files.readAttributes(file, name);
+ if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
+ throw new RuntimeException("Unexpected attribute value");
+ map = Files.readAttributes(file, "user:*");
+ if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
+ throw new RuntimeException("Unexpected attribute value");
+ }
+
+ static void miscTests(final Path file) throws IOException {
+ final UserDefinedFileAttributeView view =
+ Files.getFileAttributeView(file, UserDefinedFileAttributeView.class);
+ view.write(ATTR_NAME, ByteBuffer.wrap(ATTR_VALUE.getBytes()));
+
+ // NullPointerException
+ final ByteBuffer buf = ByteBuffer.allocate(100);
+
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.read(null, buf);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.read(ATTR_NAME, null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.write(null, buf);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.write(ATTR_NAME, null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.size(null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ view.delete(null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.getAttribute(file, null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.getAttribute(file, "user:" + ATTR_NAME, (LinkOption[])null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.setAttribute(file, "user:" + ATTR_NAME, null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.setAttribute(file, null, new byte[0]);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.setAttribute(file, "user: " + ATTR_NAME, new byte[0], (LinkOption[])null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.readAttributes(file, (String)null);
+ }});
+ expectNullPointerException(new Task() {
+ public void run() throws IOException {
+ Files.readAttributes(file, "*", (LinkOption[])null);
+ }});
+
+ // Read-only buffer
+ tryCatch(IllegalArgumentException.class, new Task() {
+ public void run() throws IOException {
+ ByteBuffer buf = ByteBuffer.wrap(ATTR_VALUE.getBytes()).asReadOnlyBuffer();
+ view.write(ATTR_NAME, buf);
+ buf.flip();
+ view.read(ATTR_NAME, buf);
+ }});
+
+ // Zero bytes remaining
+ tryCatch(IOException.class, new Task() {
+ public void run() throws IOException {
+ ByteBuffer buf = buf = ByteBuffer.allocateDirect(100);
+ buf.position(buf.capacity());
+ view.read(ATTR_NAME, buf);
+ }});
+ }
+
+ // Android-changed: Removed args & added @Test
+ @Test
+ public static void main() throws IOException {
+ // create temporary directory to run tests
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ if (!Files.getFileStore(dir).supportsFileAttributeView("user")) {
+ System.out.println("UserDefinedFileAttributeView not supported - skip test");
+ return;
+ }
+
+ // test access to user defined attributes of regular file
+ Path file = dir.resolve("foo.html");
+ Files.createFile(file);
+ try {
+ test(file);
+ } finally {
+ Files.delete(file);
+ }
+
+ // test access to user defined attributes of directory
+ Path subdir = dir.resolve("foo");
+ Files.createDirectory(subdir);
+ try {
+ test(subdir);
+ } finally {
+ Files.delete(subdir);
+ }
+
+ // test access to user defined attributes of sym link
+ if (TestUtil.supportsLinks(dir)) {
+ Path target = dir.resolve("doesnotexist");
+ Path link = dir.resolve("link");
+ Files.createSymbolicLink(link, target);
+ try {
+ test(link, NOFOLLOW_LINKS);
+ } catch (IOException x) {
+ // access to attributes of sym link may not be supported
+ } finally {
+ Files.delete(link);
+ }
+ }
+
+ // misc. tests
+ try {
+ file = dir.resolve("foo.txt");
+ Files.createFile(file);
+ miscTests(dir);
+ } finally {
+ Files.delete(file);
+ }
+
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+ }
diff --git a/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java b/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
index 1ef3a5b..23ef184 100644
--- a/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
+++ b/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
@@ -99,7 +99,7 @@
assertEquals(locale.toString(), "ja_JP_#u-ca-japanese", "Unexpected locale");
// Android changed: Android doesn't return the Japanese Imperial Calendar from getInstance.
- Calendar cal = java.util.Calendar.getJapanesImperialInstance(TimeZone.getDefault(), locale);
+ Calendar cal = Calendar.getJapaneseImperialInstance(TimeZone.getDefault(), locale);
assertEquals(cal.getCalendarType(), "japanese", "Unexpected calendar type");
JapaneseDate jDate = JapaneseChronology.INSTANCE.date(isoStartDate);
diff --git a/ojluni/src/test/java/time/test/java/util/TestFormatter.java b/ojluni/src/test/java/time/test/java/util/TestFormatter.java
index afd5fc4..05cb2e7 100644
--- a/ojluni/src/test/java/time/test/java/util/TestFormatter.java
+++ b/ojluni/src/test/java/time/test/java/util/TestFormatter.java
@@ -111,7 +111,7 @@
TimeZone tz = TimeZone.getTimeZone(zdt.getZone());
Calendar cal;
if (calLocale.getLanguage().equals("ja")) {
- cal = Calendar.getJapanesImperialInstance(tz, calLocale);
+ cal = Calendar.getJapaneseImperialInstance(tz, calLocale);
} else {
cal = Calendar.getInstance(tz, calLocale);
}
diff --git a/openjdk_java_files.mk b/openjdk_java_files.mk
index 50c4b84..4285911 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -114,6 +114,7 @@
ojluni/src/main/java/java/lang/AssertionError.java \
ojluni/src/main/java/java/lang/AutoCloseable.java \
ojluni/src/main/java/java/lang/Boolean.java \
+ ojluni/src/main/java/java/lang/BootstrapMethodError.java \
ojluni/src/main/java/java/lang/Byte.java \
ojluni/src/main/java/java/lang/Character.java \
ojluni/src/main/java/java/lang/CharSequence.java \
@@ -240,6 +241,8 @@
ojluni/src/main/java/java/lang/VirtualMachineError.java \
ojluni/src/main/java/java/lang/Void.java \
ojluni/src/main/java/java/lang/invoke/LambdaConversionException.java \
+ ojluni/src/main/java/java/lang/invoke/CallSite.java \
+ ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java \
ojluni/src/main/java/java/lang/invoke/MethodHandle.java \
ojluni/src/main/java/java/lang/invoke/MethodHandles.java \
ojluni/src/main/java/java/lang/invoke/MethodHandleImpl.java \
@@ -247,8 +250,10 @@
ojluni/src/main/java/java/lang/invoke/MethodHandleStatics.java \
ojluni/src/main/java/java/lang/invoke/MethodType.java \
ojluni/src/main/java/java/lang/invoke/MethodTypeForm.java \
+ ojluni/src/main/java/java/lang/invoke/MutableCallSite.java \
ojluni/src/main/java/java/lang/invoke/Stable.java \
ojluni/src/main/java/java/lang/invoke/Transformers.java \
+ ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java \
ojluni/src/main/java/java/lang/invoke/WrongMethodTypeException.java \
ojluni/src/main/java/java/net/AbstractPlainDatagramSocketImpl.java \
ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java \
@@ -1744,10 +1749,10 @@
# On older platforms : Both sets of stub files are used and core-oj does not contain
# any of these classes.
openjdk_lambda_stub_files := \
- ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
ojluni/src/lambda/java/java/lang/invoke/LambdaMetafactory.java \
ojluni/src/lambda/java/java/lang/invoke/SerializedLambda.java
openjdk_lambda_duplicate_stub_files := \
+ ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
ojluni/src/lambda/java/java/lang/invoke/MethodHandles.java \
ojluni/src/lambda/java/java/lang/invoke/LambdaConversionException.java \
ojluni/src/lambda/java/java/lang/invoke/MethodHandle.java \
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index a1417c3..8bc9937 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -16,6 +16,10 @@
package libcore.java.security;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import java.security.Security;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
@@ -36,7 +40,6 @@
import java.util.Set;
import javax.crypto.spec.DHPrivateKeySpec;
import javax.crypto.spec.DHPublicKeySpec;
-import junit.framework.Assert;
/**
* This class defines expected string names for protocols, key types,
@@ -63,7 +66,7 @@
* Java ™ PKCS#11 Reference Guide
* </a>.
*/
-public final class StandardNames extends Assert {
+public final class StandardNames {
public static final boolean IS_RI
= !"Dalvik Core Library".equals(System.getProperty("java.specification.name"));
@@ -107,7 +110,7 @@
private static void provide(String type, String algorithm) {
Set<String> algorithms = PROVIDER_ALGORITHMS.get(type);
if (algorithms == null) {
- algorithms = new HashSet();
+ algorithms = new HashSet<String>();
PROVIDER_ALGORITHMS.put(type, algorithms);
}
assertTrue("Duplicate " + type + " " + algorithm,
@@ -490,6 +493,16 @@
provide("Cipher", "PBEWITHSHAAND40BITRC2-CBC");
provide("Cipher", "PBEWITHSHAAND40BITRC4");
provide("Cipher", "PBEWITHSHAANDTWOFISH-CBC");
+ provide("Cipher", "PBEWithHmacSHA1AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA224AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA256AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA384AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA512AndAES_128");
+ provide("Cipher", "PBEWithHmacSHA1AndAES_256");
+ provide("Cipher", "PBEWithHmacSHA224AndAES_256");
+ provide("Cipher", "PBEWithHmacSHA256AndAES_256");
+ provide("Cipher", "PBEWithHmacSHA384AndAES_256");
+ provide("Cipher", "PBEWithHmacSHA512AndAES_256");
provide("Mac", "PBEWITHHMACSHA");
provide("Mac", "PBEWITHHMACSHA1");
provide("SecretKeyFactory", "PBEWITHHMACSHA1");
@@ -529,6 +542,20 @@
provide("Cipher", "AES/OFB/NOPADDING");
provide("Cipher", "AES/OFB/PKCS5PADDING");
provide("Cipher", "AES/OFB/PKCS7PADDING");
+ provide("Cipher", "AES_128/CBC/NOPADDING");
+ provide("Cipher", "AES_128/CBC/PKCS5PADDING");
+ provide("Cipher", "AES_128/CBC/PKCS7PADDING");
+ provide("Cipher", "AES_128/ECB/NOPADDING");
+ provide("Cipher", "AES_128/ECB/PKCS5PADDING");
+ provide("Cipher", "AES_128/ECB/PKCS7PADDING");
+ provide("Cipher", "AES_128/GCM/NOPADDING");
+ provide("Cipher", "AES_256/CBC/NOPADDING");
+ provide("Cipher", "AES_256/CBC/PKCS5PADDING");
+ provide("Cipher", "AES_256/CBC/PKCS7PADDING");
+ provide("Cipher", "AES_256/ECB/NOPADDING");
+ provide("Cipher", "AES_256/ECB/PKCS5PADDING");
+ provide("Cipher", "AES_256/ECB/PKCS7PADDING");
+ provide("Cipher", "AES_256/GCM/NOPADDING");
provide("Cipher", "DESEDE/CBC/NOPADDING");
provide("Cipher", "DESEDE/CBC/PKCS5PADDING");
provide("Cipher", "DESEDE/CBC/PKCS7PADDING");
@@ -576,7 +603,7 @@
unprovide("AlgorithmParameters", "PBEWithMD5AndDES"); // 1.2.840.113549.1.5.3
// EC support
- // provide("AlgorithmParameters", "EC");
+ provide("AlgorithmParameters", "EC");
provide("KeyAgreement", "ECDH");
provide("KeyFactory", "EC");
provide("KeyPairGenerator", "EC");
diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java
index ef62a44..3829dc1 100644
--- a/support/src/test/java/libcore/java/security/TestKeyStore.java
+++ b/support/src/test/java/libcore/java/security/TestKeyStore.java
@@ -16,10 +16,14 @@
package libcore.java.security;
+import static org.junit.Assert.assertEquals;
+
import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.x500.X500Name;
import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
import com.android.org.bouncycastle.asn1.x509.CRLReason;
import com.android.org.bouncycastle.asn1.x509.ExtendedKeyUsage;
+import com.android.org.bouncycastle.asn1.x509.Extension;
import com.android.org.bouncycastle.asn1.x509.GeneralName;
import com.android.org.bouncycastle.asn1.x509.GeneralNames;
import com.android.org.bouncycastle.asn1.x509.GeneralSubtree;
@@ -27,23 +31,20 @@
import com.android.org.bouncycastle.asn1.x509.KeyUsage;
import com.android.org.bouncycastle.asn1.x509.NameConstraints;
import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.org.bouncycastle.asn1.x509.X509Extensions;
import com.android.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.org.bouncycastle.cert.X509v3CertificateBuilder;
import com.android.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import com.android.org.bouncycastle.cert.ocsp.BasicOCSPResp;
import com.android.org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
import com.android.org.bouncycastle.cert.ocsp.CertificateID;
import com.android.org.bouncycastle.cert.ocsp.CertificateStatus;
-import com.android.org.bouncycastle.cert.ocsp.OCSPException;
import com.android.org.bouncycastle.cert.ocsp.OCSPResp;
import com.android.org.bouncycastle.cert.ocsp.OCSPRespBuilder;
import com.android.org.bouncycastle.cert.ocsp.RevokedStatus;
import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.android.org.bouncycastle.operator.DigestCalculatorProvider;
-import com.android.org.bouncycastle.operator.OperatorCreationException;
import com.android.org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import com.android.org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintStream;
@@ -64,7 +65,6 @@
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -79,7 +79,6 @@
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.x500.X500Principal;
-import junit.framework.Assert;
import libcore.javax.net.ssl.TestKeyManager;
import libcore.javax.net.ssl.TestTrustManager;
@@ -90,7 +89,7 @@
* Creating a key store is relatively slow, so a singleton instance is
* accessible via TestKeyStore.get().
*/
-public final class TestKeyStore extends Assert {
+public final class TestKeyStore {
/** Size of DSA keys to generate for testing. */
private static final int DSA_KEY_SIZE_BITS = 1024;
@@ -156,11 +155,9 @@
}
}
- private static final boolean TEST_MANAGERS = true;
private static final byte[] LOCAL_HOST_ADDRESS = { 127, 0, 0, 1 };
private static final String LOCAL_HOST_NAME = "localhost";
-
public final KeyStore keyStore;
public final char[] storePassword;
public final char[] keyPassword;
@@ -707,43 +704,40 @@
throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
}
- X509V3CertificateGenerator x509cg = new X509V3CertificateGenerator();
- x509cg.setSubjectDN(subject);
- x509cg.setIssuerDN(issuer);
- x509cg.setNotBefore(start);
- x509cg.setNotAfter(end);
- x509cg.setPublicKey(publicKey);
- x509cg.setSignatureAlgorithm(signatureAlgorithm);
if (serialNumber == null) {
byte[] serialBytes = new byte[16];
new SecureRandom().nextBytes(serialBytes);
serialNumber = new BigInteger(1, serialBytes);
}
- x509cg.setSerialNumber(serialNumber);
+
+ X509v3CertificateBuilder x509cg = new X509v3CertificateBuilder(
+ X500Name.getInstance(issuer.getEncoded()), serialNumber, start, end,
+ X500Name.getInstance(subject.getEncoded()),
+ SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
if (keyUsage != 0) {
- x509cg.addExtension(X509Extensions.KeyUsage,
+ x509cg.addExtension(Extension.keyUsage,
true,
new KeyUsage(keyUsage));
}
if (ca) {
- x509cg.addExtension(X509Extensions.BasicConstraints,
+ x509cg.addExtension(Extension.basicConstraints,
true,
new BasicConstraints(true));
}
for (int i = 0; i < extendedKeyUsages.size(); i++) {
KeyPurposeId keyPurposeId = extendedKeyUsages.get(i);
boolean critical = criticalExtendedKeyUsages.get(i);
- x509cg.addExtension(X509Extensions.ExtendedKeyUsage,
+ x509cg.addExtension(Extension.extendedKeyUsage,
critical,
new ExtendedKeyUsage(keyPurposeId));
}
for (GeneralName subjectAltName : subjectAltNames) {
- x509cg.addExtension(X509Extensions.SubjectAlternativeName,
+ x509cg.addExtension(Extension.subjectAlternativeName,
false,
new GeneralNames(subjectAltName).getEncoded());
}
if (!permittedNameConstraints.isEmpty() || !excludedNameConstraints.isEmpty()) {
- x509cg.addExtension(X509Extensions.NameConstraints,
+ x509cg.addExtension(Extension.nameConstraints,
true,
new NameConstraints(permittedNameConstraints.toArray(
new GeneralSubtree[
@@ -753,7 +747,12 @@
excludedNameConstraints.size()])));
}
- X509Certificate x509c = x509cg.generateX509Certificate(privateKey);
+ X509CertificateHolder x509holder = x509cg.build(
+ new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey));
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ X509Certificate x509c = (X509Certificate) certFactory.generateCertificate(
+ new ByteArrayInputStream(x509holder.getEncoded()));
+
if (StandardNames.IS_RI) {
/*
* The RI can't handle the BC EC signature algorithm
diff --git a/support/src/test/java/org/apache/harmony/testframework/serialization/SerializationTest.java b/support/src/test/java/org/apache/harmony/testframework/serialization/SerializationTest.java
index 4f0b90b..005e931 100644
--- a/support/src/test/java/org/apache/harmony/testframework/serialization/SerializationTest.java
+++ b/support/src/test/java/org/apache/harmony/testframework/serialization/SerializationTest.java
@@ -266,7 +266,7 @@
* @param object - object to be compared
* @return object's comparator
*/
- public static SerializableAssert defineComparator(TestCase test, Object object)
+ public static SerializableAssert defineComparator(Object test, Object object)
throws Exception {
if (test instanceof SerializableAssert) {
@@ -300,7 +300,7 @@
* @param test - test case
* @param object - to be compared
*/
- public static void verifyGolden(TestCase test, Object object) throws Exception {
+ public static void verifyGolden(Object test, Object object) throws Exception {
verifyGolden(test, object, defineComparator(test, object));
}
@@ -316,7 +316,7 @@
* @param object - to be compared
* @param comparator - for comparing (de)serialized objects
*/
- public static void verifyGolden(TestCase test, Object object, SerializableAssert comparator)
+ public static void verifyGolden(Object test, Object object, SerializableAssert comparator)
throws Exception {
assertNotNull("Null comparator", comparator);
Serializable deserialized = getObject(test, ".golden.ser");
@@ -333,7 +333,7 @@
* @param test - test case
* @param objects - array of objects to be compared
*/
- public static void verifyGolden(TestCase test, Object[] objects) throws Exception {
+ public static void verifyGolden(Object test, Object[] objects) throws Exception {
assertFalse("Empty array", objects.length == 0);
verifyGolden(test, objects, defineComparator(test, objects[0]));
}
@@ -353,7 +353,7 @@
* @param objects - array of objects to be compared
* @param comparator - for comparing (de)serialized objects
*/
- public static void verifyGolden(TestCase test, Object[] objects, SerializableAssert comparator)
+ public static void verifyGolden(Object test, Object[] objects, SerializableAssert comparator)
throws Exception {
assertFalse("Empty array", objects.length == 0);
for (int i = 0; i < objects.length; i++) {
@@ -420,7 +420,7 @@
}
}
- private static Serializable getObject(TestCase test, String toAppend) throws Exception {
+ private static Serializable getObject(Object test, String toAppend) throws Exception {
StringBuilder path = new StringBuilder("/serialization");
path.append(File.separatorChar);
path.append(test.getClass().getName().replace('.', File.separatorChar));
diff --git a/tzdata/tools/Android.mk b/tzdata/tools/Android.mk
deleted file mode 100644
index 906d866..0000000
--- a/tzdata/tools/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Library of tools classes for tzdata updates. Not required on device, except in tests.
-include $(CLEAR_VARS)
-LOCAL_MODULE := tzdata_tools
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
-LOCAL_JAVACFLAGS := -encoding UTF-8
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tzdata/tools/createIcuUpdateResources.sh b/tzdata/tools/createIcuUpdateResources.sh
deleted file mode 100755
index 2db7132..0000000
--- a/tzdata/tools/createIcuUpdateResources.sh
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/bash
-#
-# A script that generates an ICU data file containing just timezone rules data.
-# The file can be used to provide time zone rules updates for compatible
-# devices. Note: Only the rules are contained and new timezones will not have
-# the translations.
-#
-# Usage:
-# ./createIcuUpdateResources.sh <tzdata tar.gz file> <ICU version>
-#
-# e.g.
-# ./createIcuUpdateResources.sh ~/Downloads/tzdata2015b.tar.gz 55
-#
-# After execution the file is generated.
-
-if (( $# != 2 )); then
- echo "Missing arguments"
- echo "Usage:"
- echo "./createIcuUpdateResources.sh <tzdata tar.gz file> <ICU version>"
- exit 1
-fi
-
-if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
- echo "Configure your environment with build/envsetup.sh and lunch"
- exit 1
-fi
-
-TZ_DATA_FILE=$1
-ICU_VERSION=$2
-
-if [[ ! -f ${TZ_DATA_FILE} ]]; then
- echo "${TZ_DATA_FILE} not found"
- exit 1
-fi
-
-# Keep track of the original working dir. Must be the "tools" dir.
-START_DIR=`pwd`
-ICU_DIR=${ANDROID_BUILD_TOP}/external/icu/icu4c/source
-BUILD_DIR=${START_DIR}/icu_build
-
-# Fail if anything below fails
-set -e
-
-rm -rf ${BUILD_DIR}
-mkdir -p ${BUILD_DIR}
-cd ${BUILD_DIR}
-
-# Configure the build
-${ICU_DIR}/runConfigureICU Linux
-mkdir -p ${BUILD_DIR}/bin
-cd ${BUILD_DIR}/tools/tzcode
-ln -s ${ICU_DIR}/tools/tzcode/icuregions ./icuregions
-ln -s ${ICU_DIR}/tools/tzcode/icuzones ./icuzones
-cp ${TZ_DATA_FILE} .
-
-# Make the tools
-make
-
-# Then make the whole thing
-cd ${BUILD_DIR}
-make -j32
-
-# Generate the tzdata.lst file used to configure which files are included.
-ICU_LIB_DIR=${BUILD_DIR}/lib
-BIN_DIR=${BUILD_DIR}/bin
-TZ_FILES=tzdata.lst
-
-echo metaZones.res > ${TZ_FILES}
-echo timezoneTypes.res >> ${TZ_FILES}
-echo windowsZones.res >> ${TZ_FILES}
-echo zoneinfo64.res >> ${TZ_FILES}
-
-# Copy all the .res files we need here a from, e.g. ./data/out/build/icudt55l
-RES_DIR=data/out/build/icudt${ICU_VERSION}l
-cp ${RES_DIR}/metaZones.res ${BUILD_DIR}
-cp ${RES_DIR}/timezoneTypes.res ${BUILD_DIR}
-cp ${RES_DIR}/windowsZones.res ${BUILD_DIR}
-cp ${RES_DIR}/zoneinfo64.res ${BUILD_DIR}
-
-# This is the package name required for the .dat file to be accepted by ICU.
-# This also affects the generated file name.
-ICU_PACKAGE=icudt${ICU_VERSION}l
-
-# Create the file
-LD_LIBRARY_PATH=${ICU_LIB_DIR} ${BIN_DIR}/pkgdata -F -m common -v -T . -d . -p ${ICU_PACKAGE} ${TZ_FILES}
-cp ${ICU_PACKAGE}.dat ${START_DIR}/icu_tzdata.dat
-
-# Copy the file to the original working dir.
-echo File can be found here: ${START_DIR}/icu_tzdata.dat
diff --git a/tzdata/tools/createTzDataBundle.sh b/tzdata/tools/createTzDataBundle.sh
deleted file mode 100755
index 05646fc..0000000
--- a/tzdata/tools/createTzDataBundle.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-
-# A script to generate TZ data updates.
-#
-# Usage: ./createTzDataBundle.sh <tzupdate.properties file> <output file>
-# See libcore.tzdata.update.tools.CreateTzDataBundle for more information.
-
-TOOLS_DIR=src/main/libcore/tzdata/update/tools
-UPDATE_DIR=../update/src/main/libcore/tzdata/update
-GEN_DIR=./gen
-
-# Fail if anything below fails
-set -e
-
-rm -rf ${GEN_DIR}
-mkdir -p ${GEN_DIR}
-
-javac \
- ${TOOLS_DIR}/CreateTzDataBundle.java \
- ${TOOLS_DIR}/TzDataBundleBuilder.java \
- ${UPDATE_DIR}/ConfigBundle.java \
- ${UPDATE_DIR}/FileUtils.java \
- -d ${GEN_DIR}
-
-java -cp ${GEN_DIR} libcore.tzdata.update.tools.CreateTzDataBundle $@
diff --git a/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java b/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java
deleted file mode 100644
index cdb004a..0000000
--- a/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.tzdata.update.tools;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.util.Properties;
-import libcore.tzdata.update.ConfigBundle;
-import libcore.tzdata.update.FileUtils;
-
-/**
- * A command-line tool for creating a TZ data update bundle.
- *
- * Args:
- * tzdata.properties file - the file describing the bundle (see template file in tzdata/tools)
- * output file - the name of the file to be generated
- */
-public class CreateTzDataBundle {
-
- private CreateTzDataBundle() {}
-
- public static void main(String[] args) throws Exception {
- if (args.length != 2) {
- printUsage();
- System.exit(1);
- }
- File f = new File(args[0]);
- if (!f.exists()) {
- System.err.println("Properties file " + f + " not found");
- printUsage();
- System.exit(2);
- }
- Properties p = loadProperties(f);
- TzDataBundleBuilder builder = new TzDataBundleBuilder()
- .setTzDataVersion(getMandatoryProperty(p, "tzdata.version"))
- .addBionicTzData(getMandatoryPropertyFile(p, "bionic.file"))
- .addIcuTzData(getMandatoryPropertyFile(p, "icu.file"));
-
- int i = 1;
- while (true) {
- String localFileNameProperty = "checksum.file.local." + i;
- String localFileName = p.getProperty(localFileNameProperty);
- String onDeviceFileNameProperty = "checksum.file.ondevice." + i;
- String onDeviceFileName = p.getProperty(onDeviceFileNameProperty);
- boolean foundLocalFileNameProperty = localFileName != null;
- boolean foundOnDeviceFileNameProperty = onDeviceFileName != null;
- if (!foundLocalFileNameProperty && !foundOnDeviceFileNameProperty) {
- break;
- } else if (foundLocalFileNameProperty != foundOnDeviceFileNameProperty) {
- System.out.println("Properties file must specify both, or neither of: "
- + localFileNameProperty + " and " + onDeviceFileNameProperty);
- System.exit(5);
- }
-
- long checksum = FileUtils.calculateChecksum(new File(localFileName));
- builder.addChecksum(onDeviceFileName, checksum);
- i++;
- }
- if (i == 1) {
- // For safety we enforce >= 1 checksum entry. The installer does not require it.
- System.out.println("There must be at least one checksum file");
- System.exit(6);
- }
- System.out.println("Update contains checksums for " + (i-1) + " files");
-
- ConfigBundle bundle = builder.build();
- File outputFile = new File(args[1]);
- try (OutputStream os = new FileOutputStream(outputFile)) {
- os.write(bundle.getBundleBytes());
- }
- System.out.println("Wrote: " + outputFile);
- }
-
- private static File getMandatoryPropertyFile(Properties p, String propertyName) {
- String fileName = getMandatoryProperty(p, propertyName);
- File file = new File(fileName);
- if (!file.exists()) {
- System.out.println(
- "Missing file: " + file + " for property " + propertyName + " does not exist.");
- printUsage();
- System.exit(4);
- }
- return file;
- }
-
- private static String getMandatoryProperty(Properties p, String propertyName) {
- String value = p.getProperty(propertyName);
- if (value == null) {
- System.out.println("Missing property: " + propertyName);
- printUsage();
- System.exit(3);
- }
- return value;
- }
-
- private static Properties loadProperties(File f) throws IOException {
- Properties p = new Properties();
- try (Reader reader = new InputStreamReader(new FileInputStream(f))) {
- p.load(reader);
- }
- return p;
- }
-
- private static void printUsage() {
- System.out.println("Usage:");
- System.out.println("\t" + CreateTzDataBundle.class.getName() +
- " <tzupdate.properties file> <output file>");
- }
-}
diff --git a/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java b/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java
deleted file mode 100644
index 3550c6f..0000000
--- a/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.tzdata.update.tools;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-import libcore.tzdata.update.ConfigBundle;
-
-/**
- * A class for creating a {@link ConfigBundle} containing timezone update data.
- */
-public final class TzDataBundleBuilder {
-
- private String tzDataVersion;
- private StringBuilder checksumsFileContent = new StringBuilder();
- private File zoneInfoFile;
- private File icuTzDataFile;
-
- public TzDataBundleBuilder setTzDataVersion(String tzDataVersion) {
- this.tzDataVersion = tzDataVersion;
- return this;
- }
-
- public TzDataBundleBuilder addChecksum(String fileName, long checksum) {
- checksumsFileContent.append(Long.toString(checksum))
- .append(',')
- .append(fileName)
- .append('\n');
- return this;
- }
-
- public TzDataBundleBuilder addBionicTzData(File zoneInfoFile) {
- this.zoneInfoFile = zoneInfoFile;
- return this;
- }
-
- public TzDataBundleBuilder addIcuTzData(File icuTzDataFile) {
- this.icuTzDataFile = icuTzDataFile;
- return this;
- }
-
- /**
- * Builds a {@link libcore.tzdata.update.ConfigBundle}.
- */
- public ConfigBundle build() throws IOException {
- if (tzDataVersion == null) {
- throw new IllegalStateException("Missing tzDataVersion");
- }
- if (zoneInfoFile == null) {
- throw new IllegalStateException("Missing zoneInfo file");
- }
-
- return buildUnvalidated();
- }
-
- // For use in tests.
- public TzDataBundleBuilder clearChecksumEntries() {
- checksumsFileContent.setLength(0);
- return this;
- }
-
- // For use in tests.
- public TzDataBundleBuilder clearBionicTzData() {
- this.zoneInfoFile = null;
- return this;
- }
-
- /**
- * For use in tests. Use {@link #build()}.
- */
- public ConfigBundle buildUnvalidated() throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try (ZipOutputStream zos = new ZipOutputStream(baos)) {
- addZipEntry(zos, ConfigBundle.CHECKSUMS_FILE_NAME,
- checksumsFileContent.toString().getBytes(StandardCharsets.UTF_8));
- if (tzDataVersion != null) {
- addZipEntry(zos, ConfigBundle.TZ_DATA_VERSION_FILE_NAME,
- tzDataVersion.getBytes(StandardCharsets.UTF_8));
- }
- if (zoneInfoFile != null) {
- addZipEntry(zos, ConfigBundle.ZONEINFO_FILE_NAME,
- readFileAsByteArray(zoneInfoFile));
- }
- if (icuTzDataFile != null) {
- addZipEntry(zos, ConfigBundle.ICU_DATA_FILE_NAME,
- readFileAsByteArray(icuTzDataFile));
- }
- }
- return new ConfigBundle(baos.toByteArray());
- }
-
- private static void addZipEntry(ZipOutputStream zos, String name, byte[] content)
- throws IOException {
- ZipEntry zipEntry = new ZipEntry(name);
- zipEntry.setSize(content.length);
- zos.putNextEntry(zipEntry);
- zos.write(content);
- zos.closeEntry();
- }
-
- /**
- * Returns the contents of 'path' as a byte array.
- */
- public static byte[] readFileAsByteArray(File file) throws IOException {
- byte[] buffer = new byte[8192];
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try (FileInputStream fis = new FileInputStream(file)) {
- int count;
- while ((count = fis.read(buffer)) != -1) {
- baos.write(buffer, 0, count);
- }
- }
- return baos.toByteArray();
- }
-}
-
diff --git a/tzdata/tools/testing/prepareTzDataUpdates.sh b/tzdata/tools/testing/prepareTzDataUpdates.sh
deleted file mode 100755
index a9fe10c..0000000
--- a/tzdata/tools/testing/prepareTzDataUpdates.sh
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/bin/bash
-
-# Used as part of manual testing of tzdata update mechanism.
-
-if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
- echo "Configure your environment with build/envsetup.sh and lunch"
- exit 1
-fi
-
-# Fail if anything below fails
-set -e
-
-cd $ANDROID_BUILD_TOP
-
-# Get the ICU version by looking at the stubdata file
-ICU_STUB_FILE=$(ls external/icu/icu4c/source/stubdata/icudt*l.dat)
-if [ $(echo "$ICU_STUB_FILE" | wc -l) -ne 1 ]; then
- echo "Could not find unique ICU version from external/icu/icu4c/source/stubdata/icudt*l.dat"
- exit 1
-fi
-
-ICU_VERSION=${ICU_STUB_FILE##*icudt}
-ICU_VERSION=${ICU_VERSION%l.dat}
-
-echo "Current ICU version is ${ICU_VERSION}"
-
-# Get the current tzdata version and find both the previous and new versions.
-TZDATA=libc/zoneinfo/tzdata
-
-TZHEADER=$(head -n1 bionic/$TZDATA | cut -c1-11)
-
-TZ_CURRENT=${TZHEADER:6}
-
-let currentYear=${TZ_CURRENT:0:4}
-update=${TZ_CURRENT:4:1}
-
-let currentUpdate=$(LC_CTYPE=C printf '%d' "'$update")
-let previousUpdate=currentUpdate-1
-if [ $previousUpdate -lt 97 ]; then
- let previousYear=currentYear-1
- PREVIOUSCOMMIT=$(cd bionic; git log --format=oneline ${TZDATA} | grep -E "${previousYear}.$" | head -n1)
- TZ_PREVIOUS=$(echo $PREVIOUSCOMMIT | sed "s|.*\(${previousYear}.\)\$|\1|")
-else
- TZ_PREVIOUS=${currentYear}$(printf "\\$(printf '%03o' "${previousUpdate}")")
- PREVIOUSCOMMIT=$(cd bionic; git log --format=oneline ${TZDATA} | grep -E "${TZ_PREVIOUS}$" | head -n1)
-fi
-
-let nextUpdate=currentUpdate+1
-TZ_NEXT=${currentYear}$(printf "\\$(printf '%03o' "${nextUpdate}")")
-
-echo "Current version of bionic/${TZDATA} is ${TZ_CURRENT}"
-echo "Previous version of bionic/${TZDATA} is ${TZ_PREVIOUS} from commit:"
-echo " ${PREVIOUSCOMMIT}"
-echo "Next version of bionic/${TZDATA} is ${TZ_NEXT}"
-
-TMP=$(mktemp -d)
-
-TZ_PREVIOUS_SHA=${PREVIOUSCOMMIT%% *}
-
-TMP_PREVIOUS=${TMP}/${TZ_PREVIOUS}_test
-mkdir -p ${TMP_PREVIOUS}
-
-echo "Copied ${TZ_PREVIOUS} tzdata to ${TMP_PREVIOUS}"
-(cd bionic; git show ${TZ_PREVIOUS_SHA}:${TZDATA} > ${TMP_PREVIOUS}/tzdata)
-
-cd libcore/tzdata/tools
-
-################################################################
-# Preparing previous update version.
-################################################################
-
-# Create an icu_tzdata.dat for the previous tzdata version.
-# Download the archive for the previous tzdata version.
-PREVIOUS_TAR_GZ="tzdata${TZ_PREVIOUS}.tar.gz"
-TMP_PREVIOUS_TAR_GZ=${TMP}/${PREVIOUS_TAR_GZ}
-IANA_PREVIOUS_URL="ftp://ftp.iana.org/tz/releases/${PREVIOUS_TAR_GZ}"
-echo "Downloading archive for ${TZ_PREVIOUS} version from ${IANA_PREVIOUS_URL}"
-wget -O ${TMP_PREVIOUS_TAR_GZ} ftp://ftp.iana.org/tz/releases/tzdata2016d.tar.gz -o ${TMP}/${PREVIOUS_TAR_GZ}.log
-
-ICU_UPDATE_RESOURCES_LOG=${TMP}/icuUpdateResources.log
-echo "Creating icu_tzdata.dat file for ${TZ_PREVIOUS}/ICU ${ICU_VERSION}, may take a while, check ${ICU_UPDATE_RESOURCES_LOG} for details"
-./createIcuUpdateResources.sh ${TMP_PREVIOUS_TAR_GZ} ${ICU_VERSION} &> ${ICU_UPDATE_RESOURCES_LOG}
-mv icu_tzdata.dat ${TMP_PREVIOUS}
-
-TZ_PREVIOUS_UPDATE_PROPERTIES=${TMP}/tzupdate.properties.${TZ_PREVIOUS}
-cat > ${TZ_PREVIOUS_UPDATE_PROPERTIES} <<EOF
-tzdata.version=${TZ_PREVIOUS}
-bionic.file=${TMP_PREVIOUS}/tzdata
-icu.file=${TMP_PREVIOUS}/icu_tzdata.dat
-
-checksum.file.local.1=../../../bionic/libc/zoneinfo/tzdata
-checksum.file.ondevice.1=/system/usr/share/zoneinfo/tzdata
-checksum.file.local.2=../../../external/icu/icu4c/source/stubdata/icudt${ICU_VERSION}l.dat
-checksum.file.ondevice.2=/system/usr/icu/icudt${ICU_VERSION}l.dat
-EOF
-
-TZ_PREVIOUS_UPDATE_ZIP=update_${TZ_PREVIOUS}_test.zip
-./createTzDataBundle.sh ${TZ_PREVIOUS_UPDATE_PROPERTIES} ${TMP}/update_${TZ_PREVIOUS}_test.zip
-adb push ${TMP}/${TZ_PREVIOUS_UPDATE_ZIP} /data/local/tmp
-echo "Pushed ${TZ_PREVIOUS_UPDATE_ZIP} to /data/local/tmp"
-
-################################################################
-# Preparing next update version.
-################################################################
-
-# Replace the version number in the previous tzdata to create the next version
-TMP_NEXT=${TMP}/${TZ_NEXT}_test
-mkdir -p ${TMP_NEXT}
-sed "1s/^tzdata${TZ_PREVIOUS}/tzdata${TZ_NEXT}/" ${TMP_PREVIOUS}/tzdata > ${TMP_NEXT}/tzdata
-echo "Transformed version ${TZ_PREVIOUS} of tzdata into version ${TZ_NEXT}"
-
-# Replace the version number in the previous icu_tzdata to create the next version
-SEARCH=$(echo ${TZ_PREVIOUS} | sed "s/\(.\)/\1\\\x00/g")
-REPLACE=$(echo ${TZ_NEXT} | sed "s/\(.\)/\1\\\x00/g")
-sed "s/$SEARCH/$REPLACE/" ${TMP_PREVIOUS}/icu_tzdata.dat > ${TMP_NEXT}/icu_tzdata.dat
-echo "Transformed version ${TZ_PREVIOUS} of icu_tzdata into version ${TZ_NEXT}"
-
-TZ_NEXT_UPDATE_PROPERTIES=${TMP}/tzupdate.properties.${TZ_NEXT}
-cat > ${TZ_NEXT_UPDATE_PROPERTIES} <<EOF
-tzdata.version=${TZ_NEXT}
-bionic.file=${TMP_NEXT}/tzdata
-icu.file=${TMP_NEXT}/icu_tzdata.dat
-
-checksum.file.local.1=../../../bionic/libc/zoneinfo/tzdata
-checksum.file.ondevice.1=/system/usr/share/zoneinfo/tzdata
-checksum.file.local.2=../../../external/icu/icu4c/source/stubdata/icudt${ICU_VERSION}l.dat
-checksum.file.ondevice.2=/system/usr/icu/icudt${ICU_VERSION}l.dat
-EOF
-
-TZ_NEXT_UPDATE_ZIP=update_${TZ_NEXT}_test.zip
-./createTzDataBundle.sh ${TZ_NEXT_UPDATE_PROPERTIES} ${TMP}/update_${TZ_NEXT}_test.zip
-adb push ${TMP}/${TZ_NEXT_UPDATE_ZIP} /data/local/tmp
-echo "Pushed ${TZ_NEXT_UPDATE_ZIP} to /data/local/tmp"
-
-################################################################
-# Preparing bad update version.
-################################################################
-
-TZ_BAD=${TZ_NEXT}_bad_sum
-TZ_BAD_UPDATE_PROPERTIES=${TMP}/tzupdate.properties.${TZ_BAD}
-cat > ${TZ_BAD_UPDATE_PROPERTIES} <<EOF
-tzdata.version=${TZ_BAD}
-bionic.file=${TMP_NEXT}/tzdata
-icu.file=${TMP_NEXT}/icu_tzdata.dat
-
-checksum.file.local.1=${ANDROID_BUILD_TOP}/bionic/${TZDATA}
-checksum.file.ondevice.1=/system/usr/share/zoneinfo/tzdata
-
-# The wrong checksum
-checksum.file.local.2=${ANDROID_BUILD_TOP}/libcore/Android.mk
-checksum.file.ondevice.2=/system/usr/icu/icudt${ICU_VERSION}l.dat
-EOF
-
-TZ_BAD_UPDATE_ZIP=update_${TZ_BAD}_test.zip
-./createTzDataBundle.sh ${TZ_BAD_UPDATE_PROPERTIES} ${TMP}/update_${TZ_BAD}_test.zip
-adb push ${TMP}/${TZ_BAD_UPDATE_ZIP} /data/local/tmp
-echo "Pushed ${TZ_BAD_UPDATE_ZIP} to /data/local/tmp"
-
-################################################################
-# Preparing UpdateTestApp
-################################################################
-
-UPDATE_TEST_APP_LOG=${TMP}/updateTestApp.log
-echo "Building and installing UpdateTestApp, check ${UPDATE_TEST_APP_LOG}"
-cd $ANDROID_BUILD_TOP
-make -j -l8 UpdateTestApp &> ${UPDATE_TEST_APP_LOG}
-adb install -r ${ANDROID_PRODUCT_OUT}/data/app/UpdateTestApp/UpdateTestApp.apk &>> ${UPDATE_TEST_APP_LOG}
-
-echo
-echo "Paste the following into your shell"
-echo "OLD_TZUPDATE=${TMP}/${TZ_PREVIOUS_UPDATE_ZIP}"
-echo "NEW_TZUPDATE=${TMP}/${TZ_NEXT_UPDATE_ZIP}"
-echo "BAD_TZUPDATE=${TMP}/${TZ_BAD_UPDATE_ZIP}"
diff --git a/tzdata/tools/testing/rebootAndGrabLogs.sh b/tzdata/tools/testing/rebootAndGrabLogs.sh
deleted file mode 100755
index a85481f..0000000
--- a/tzdata/tools/testing/rebootAndGrabLogs.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-
-# Used as part of manual testing of tzdata update mechanism.
-
-PREFIX=${1-bootlogs}
-
-echo "Rebooting...."
-adb reboot && adb wait-for-device
-
-TIME=${2-5}
-LOGCAT=${PREFIX}.logcat
-echo "Dumping logcat output in ${LOGCAT}, waiting for ${TIME} seconds"
-adb logcat > ${LOGCAT} 2>/dev/null &
-LOGCAT_PID=$!
-
-sleep ${TIME}
-
-# Kill the logcat process and wait, suppresses the Terminated message
-# output by the shell.
-kill ${LOGCAT_PID}
-wait ${LOGCAT_PID} 2>/dev/null
-
-DMESG=${PREFIX}.dmesg
-echo "Dumping dmesg output in ${DMESG}"
-adb shell dmesg > ${DMESG}
diff --git a/tzdata/tools/tzupdate.properties b/tzdata/tools/tzupdate.properties
deleted file mode 100644
index e3fe002..0000000
--- a/tzdata/tools/tzupdate.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# Edit these to reflect the update files.
-
-# This should be the tzdata version. e.g. "2015a". Lexicographical sort order
-# may become important in future so if inventing interim releases only add
-# characters to the end.
-tzdata.version=
-bionic.file=
-icu.file=
-
-# Edit these as required to point to the file expected to exist on the device.
-checksum.file.local.1=../../../bionic/libc/zoneinfo/tzdata
-checksum.file.ondevice.1=/system/usr/share/zoneinfo/tzdata
-checksum.file.local.2=../../../external/icu/icu4c/source/stubdata/icudt55l.dat
-checksum.file.ondevice.2=/system/usr/icu/icudt55l.dat
diff --git a/tzdata/update/Android.mk b/tzdata/update/Android.mk
deleted file mode 100644
index 8b45b7f..0000000
--- a/tzdata/update/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Library of support classes for tzdata updates. Shared between update generation and
-# on-device code.
-include $(CLEAR_VARS)
-LOCAL_MODULE := tzdata_update
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
-LOCAL_JAVACFLAGS := -encoding UTF-8
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Tests for tzdata_update code
-include $(CLEAR_VARS)
-LOCAL_MODULE := tzdata_update-tests
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src/test)
-LOCAL_JAVACFLAGS := -encoding UTF-8
-LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update tzdata_tools junit
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java b/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java
deleted file mode 100644
index b497c85..0000000
--- a/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.tzdata.update;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-/**
- * A configuration bundle. This is a thin wrapper around some in-memory bytes representing a zip
- * archive and logic for its safe extraction.
- */
-public final class ConfigBundle {
-
- /** The name of the file inside the bundle containing the TZ data version. */
- public static final String TZ_DATA_VERSION_FILE_NAME = "tzdata_version";
-
- /** The name of the file inside the bundle containing the expected device checksums. */
- public static final String CHECKSUMS_FILE_NAME = "checksums";
-
- /** The name of the file inside the bundle containing bionic/libcore TZ data. */
- public static final String ZONEINFO_FILE_NAME = "tzdata";
-
- /** The name of the file inside the bundle containing ICU TZ data. */
- public static final String ICU_DATA_FILE_NAME = "icu/icu_tzdata.dat";
-
- private static final int BUFFER_SIZE = 8192;
-
- private final byte[] bytes;
-
- public ConfigBundle(byte[] bytes) {
- this.bytes = bytes;
- }
-
- public byte[] getBundleBytes() {
- return bytes;
- }
-
- public void extractTo(File targetDir) throws IOException {
- extractZipSafely(new ByteArrayInputStream(bytes), targetDir, true /* makeWorldReadable */);
- }
-
- /** Visible for testing */
- static void extractZipSafely(InputStream is, File targetDir, boolean makeWorldReadable)
- throws IOException {
-
- // Create the extraction dir, if needed.
- FileUtils.ensureDirectoriesExist(targetDir, makeWorldReadable);
-
- try (ZipInputStream zipInputStream = new ZipInputStream(is)) {
- byte[] buffer = new byte[BUFFER_SIZE];
- ZipEntry entry;
- while ((entry = zipInputStream.getNextEntry()) != null) {
- // Validate the entry name: make sure the unpacked file will exist beneath the
- // targetDir.
- String name = entry.getName();
- // Note, we assume that nothing will quickly insert a symlink after createSubFile()
- // that might invalidate the guarantees about name existing beneath targetDir.
- File entryFile = FileUtils.createSubFile(targetDir, name);
-
- if (entry.isDirectory()) {
- FileUtils.ensureDirectoriesExist(entryFile, makeWorldReadable);
- } else {
- // Create the path if there was no directory entry.
- if (!entryFile.getParentFile().exists()) {
- FileUtils.ensureDirectoriesExist(
- entryFile.getParentFile(), makeWorldReadable);
- }
-
- try (FileOutputStream fos = new FileOutputStream(entryFile)) {
- int count;
- while ((count = zipInputStream.read(buffer)) != -1) {
- fos.write(buffer, 0, count);
- }
- // sync to disk
- fos.getFD().sync();
- }
- // mark entryFile -rw-r--r--
- if (makeWorldReadable) {
- FileUtils.makeWorldReadable(entryFile);
- }
- }
- }
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- ConfigBundle that = (ConfigBundle) o;
-
- if (!Arrays.equals(bytes, that.bytes)) {
- return false;
- }
-
- return true;
- }
-
-}
diff --git a/tzdata/update/src/main/libcore/tzdata/update/FileUtils.java b/tzdata/update/src/main/libcore/tzdata/update/FileUtils.java
deleted file mode 100644
index 652b786..0000000
--- a/tzdata/update/src/main/libcore/tzdata/update/FileUtils.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.tzdata.update;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.zip.CRC32;
-
-/**
- * Utility methods for files operations.
- */
-public final class FileUtils {
-
- private FileUtils() {
- }
-
- /**
- * Creates a new {@link java.io.File} from the {@code parentDir} and {@code name}, but only if
- * the resulting file would exist beneath {@code parentDir}. Useful if {@code name} could
- * contain "/../" or symlinks. The returned object has a canonicalized path.
- *
- * @throws java.io.IOException if the file would not exist beneath {@code parentDir}
- */
- public static File createSubFile(File parentDir, String name) throws IOException {
- // The subFile must exist beneath parentDir. If name contains "/../" this may not be the
- // case so we check.
- File subFile = new File(parentDir, name).getCanonicalFile();
- if (!subFile.getPath().startsWith(parentDir.getCanonicalPath())) {
- throw new IOException(name + " must exist beneath " + parentDir +
- ". Canonicalized subpath: " + subFile);
- }
- return subFile;
- }
-
- /**
- * Makes sure a directory exists. If it doesn't exist, it is created. Parent directories are
- * also created as needed. If {@code makeWorldReadable} is {@code true} the directory's default
- * permissions will be set. Even when {@code makeWorldReadable} is {@code true}, only
- * directories explicitly created will have their permissions set; existing directories are
- * untouched.
- *
- * @throws IOException if the directory or one of its parents did not already exist and could
- * not be created
- */
- public static void ensureDirectoriesExist(File dir, boolean makeWorldReadable)
- throws IOException {
- LinkedList<File> dirs = new LinkedList<>();
- File currentDir = dir;
- do {
- dirs.addFirst(currentDir);
- currentDir = currentDir.getParentFile();
- } while (currentDir != null);
-
- for (File dirToCheck : dirs) {
- if (!dirToCheck.exists()) {
- if (!dirToCheck.mkdir()) {
- throw new IOException("Unable to create directory: " + dir);
- }
- if (makeWorldReadable) {
- makeDirectoryWorldAccessible(dirToCheck);
- }
- } else if (!dirToCheck.isDirectory()) {
- throw new IOException(dirToCheck + " exists but is not a directory");
- }
- }
- }
-
- public static void makeDirectoryWorldAccessible(File directory) throws IOException {
- if (!directory.isDirectory()) {
- throw new IOException(directory + " must be a directory");
- }
- makeWorldReadable(directory);
- if (!directory.setExecutable(true, false /* ownerOnly */)) {
- throw new IOException("Unable to make " + directory + " world-executable");
- }
- }
-
- public static void makeWorldReadable(File file) throws IOException {
- if (!file.setReadable(true, false /* ownerOnly */)) {
- throw new IOException("Unable to make " + file + " world-readable");
- }
- }
-
- /**
- * Calculates the checksum from the contents of a file.
- */
- public static long calculateChecksum(File file) throws IOException {
- final int BUFFER_SIZE = 8196;
- CRC32 crc32 = new CRC32();
- try (FileInputStream fis = new FileInputStream(file)) {
- byte[] buffer = new byte[BUFFER_SIZE];
- int count;
- while ((count = fis.read(buffer)) != -1) {
- crc32.update(buffer, 0, count);
- }
- }
- return crc32.getValue();
- }
-
- public static void rename(File from, File to) throws IOException {
- ensureFileDoesNotExist(to);
- if (!from.renameTo(to)) {
- throw new IOException("Unable to rename " + from + " to " + to);
- }
- }
-
- public static void ensureFileDoesNotExist(File file) throws IOException {
- if (file.exists()) {
- if (!file.isFile()) {
- throw new IOException(file + " is not a file");
- }
- doDelete(file);
- }
- }
-
- public static void doDelete(File file) throws IOException {
- if (!file.delete()) {
- throw new IOException("Unable to delete: " + file);
- }
- }
-
- public static boolean isSymlink(File file) throws IOException {
- String baseName = file.getName();
- String canonicalPathExceptBaseName =
- new File(file.getParentFile().getCanonicalFile(), baseName).getPath();
- return !file.getCanonicalPath().equals(canonicalPathExceptBaseName);
- }
-
- public static void deleteRecursive(File toDelete) throws IOException {
- if (toDelete.isDirectory()) {
- for (File file : toDelete.listFiles()) {
- if (file.isDirectory() && !FileUtils.isSymlink(file)) {
- // The isSymlink() check is important so that we don't delete files in other
- // directories: only the symlink itself.
- deleteRecursive(file);
- } else {
- // Delete symlinks to directories or files.
- FileUtils.doDelete(file);
- }
- }
- String[] remainingFiles = toDelete.list();
- if (remainingFiles.length != 0) {
- throw new IOException("Unable to delete files: " + Arrays
- .toString(remainingFiles));
- }
- }
- FileUtils.doDelete(toDelete);
- }
-
- public static boolean filesExist(File rootDir, String... fileNames) throws IOException {
- for (String fileName : fileNames) {
- File file = new File(rootDir, fileName);
- if (!file.exists()) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Read all lines from a UTF-8 encoded file, returning them as a list of strings.
- */
- public static List<String> readLines(File file) throws IOException {
- FileInputStream in = new FileInputStream(file);
- try (BufferedReader fileReader = new BufferedReader(
- new InputStreamReader(in, StandardCharsets.UTF_8));
- ) {
- List<String> lines = new ArrayList<>();
- String line;
- while ((line = fileReader.readLine()) != null) {
- lines.add(line);
- }
- return lines;
- }
- }
-}
diff --git a/tzdata/update/src/main/libcore/tzdata/update/TzDataBundleInstaller.java b/tzdata/update/src/main/libcore/tzdata/update/TzDataBundleInstaller.java
deleted file mode 100644
index df0b2a7..0000000
--- a/tzdata/update/src/main/libcore/tzdata/update/TzDataBundleInstaller.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.tzdata.update;
-
-import android.util.Slog;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * A bundle-validation / extraction class. Separate from the services code that uses it for easier
- * testing.
- */
-public final class TzDataBundleInstaller {
-
- static final String CURRENT_TZ_DATA_DIR_NAME = "current";
- static final String WORKING_DIR_NAME = "working";
- static final String OLD_TZ_DATA_DIR_NAME = "old";
-
- private final String logTag;
- private final File installDir;
-
- public TzDataBundleInstaller(String logTag, File installDir) {
- this.logTag = logTag;
- this.installDir = installDir;
- }
-
- /**
- * Install the supplied content.
- *
- * <p>Errors during unpacking or installation will throw an {@link IOException}.
- * If the content is invalid this method returns {@code false}.
- * If the installation completed successfully this method returns {@code true}.
- */
- public boolean install(byte[] content) throws IOException {
- File oldTzDataDir = new File(installDir, OLD_TZ_DATA_DIR_NAME);
- if (oldTzDataDir.exists()) {
- FileUtils.deleteRecursive(oldTzDataDir);
- }
-
- File currentTzDataDir = new File(installDir, CURRENT_TZ_DATA_DIR_NAME);
- File workingDir = new File(installDir, WORKING_DIR_NAME);
-
- Slog.i(logTag, "Applying time zone update");
- File unpackedContentDir = unpackBundle(content, workingDir);
- try {
- if (!checkBundleFilesExist(unpackedContentDir)) {
- Slog.i(logTag, "Update not applied: Bundle is missing files");
- return false;
- }
-
- if (verifySystemChecksums(unpackedContentDir)) {
- FileUtils.makeDirectoryWorldAccessible(unpackedContentDir);
-
- if (currentTzDataDir.exists()) {
- Slog.i(logTag, "Moving " + currentTzDataDir + " to " + oldTzDataDir);
- FileUtils.rename(currentTzDataDir, oldTzDataDir);
- }
- Slog.i(logTag, "Moving " + unpackedContentDir + " to " + currentTzDataDir);
- FileUtils.rename(unpackedContentDir, currentTzDataDir);
- Slog.i(logTag, "Update applied: " + currentTzDataDir + " successfully created");
- return true;
- }
- Slog.i(logTag, "Update not applied: System checksum did not match");
- return false;
- } finally {
- deleteBestEffort(oldTzDataDir);
- deleteBestEffort(unpackedContentDir);
- }
- }
-
- private void deleteBestEffort(File dir) {
- if (dir.exists()) {
- try {
- FileUtils.deleteRecursive(dir);
- } catch (IOException e) {
- // Logged but otherwise ignored.
- Slog.w(logTag, "Unable to delete " + dir, e);
- }
- }
- }
-
- private File unpackBundle(byte[] content, File targetDir) throws IOException {
- Slog.i(logTag, "Unpacking update content to: " + targetDir);
- ConfigBundle bundle = new ConfigBundle(content);
- bundle.extractTo(targetDir);
- return targetDir;
- }
-
- private boolean checkBundleFilesExist(File unpackedContentDir) throws IOException {
- Slog.i(logTag, "Verifying bundle contents");
- return FileUtils.filesExist(unpackedContentDir,
- ConfigBundle.TZ_DATA_VERSION_FILE_NAME,
- ConfigBundle.CHECKSUMS_FILE_NAME,
- ConfigBundle.ZONEINFO_FILE_NAME,
- ConfigBundle.ICU_DATA_FILE_NAME);
- }
-
- private boolean verifySystemChecksums(File unpackedContentDir) throws IOException {
- Slog.i(logTag, "Verifying system file checksums");
- File checksumsFile = new File(unpackedContentDir, ConfigBundle.CHECKSUMS_FILE_NAME);
- for (String line : FileUtils.readLines(checksumsFile)) {
- int delimiterPos = line.indexOf(',');
- if (delimiterPos <= 0 || delimiterPos == line.length() - 1) {
- throw new IOException("Bad checksum entry: " + line);
- }
- long expectedChecksum;
- try {
- expectedChecksum = Long.parseLong(line.substring(0, delimiterPos));
- } catch (NumberFormatException e) {
- throw new IOException("Invalid checksum value: " + line);
- }
- String filePath = line.substring(delimiterPos + 1);
- File file = new File(filePath);
- if (!file.exists()) {
- Slog.i(logTag, "Failed checksum test for file: " + file + ": file not found");
- return false;
- }
- long actualChecksum = FileUtils.calculateChecksum(file);
- if (actualChecksum != expectedChecksum) {
- Slog.i(logTag, "Failed checksum test for file: " + file
- + ": required=" + expectedChecksum + ", actual=" + actualChecksum);
- return false;
- }
- }
- return true;
- }
-}
diff --git a/tzdata/update/src/test/libcore/tzdata/update/ConfigBundleTest.java b/tzdata/update/src/test/libcore/tzdata/update/ConfigBundleTest.java
deleted file mode 100644
index f1325e7..0000000
--- a/tzdata/update/src/test/libcore/tzdata/update/ConfigBundleTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.tzdata.update;
-
-import junit.framework.TestCase;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-import libcore.io.IoUtils;
-
-/**
- * Tests for {@link ConfigBundle}.
- */
-public class ConfigBundleTest extends TestCase {
-
- private final List<File> testFiles = new ArrayList<>();
-
- @Override
- public void tearDown() throws Exception {
- // Delete files / directories in reverse order.
- Collections.reverse(testFiles);
- for (File tempFile : testFiles) {
- tempFile.delete();
- }
- super.tearDown();
- }
-
- public void testExtractZipSafely_goodZip() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
- addZipEntry(zipOutputStream, "/leadingSlash");
- addZipEntry(zipOutputStream, "absolute");
- addZipEntry(zipOutputStream, "subDir/../file");
- addZipEntry(zipOutputStream, "subDir/subDir/subDir/file");
- addZipEntry(zipOutputStream, "subDir/subDir2/"); // Directory entry
- addZipEntry(zipOutputStream, "subDir/../subDir3/"); // Directory entry
- }
- File dir = createTempDir();
- File targetDir = new File(dir, "target");
- TestInputStream inputStream =
- new TestInputStream(new ByteArrayInputStream(baos.toByteArray()));
- ConfigBundle.extractZipSafely(inputStream, targetDir, true /* makeWorldReadable */);
- inputStream.assertClosed();
- assertFilesExist(
- new File(targetDir, "leadingSlash"),
- new File(targetDir, "absolute"),
- new File(targetDir, "file"),
- new File(targetDir, "subDir/subDir/subDir/file"));
- assertDirsExist(
- new File(targetDir, "subDir/subDir2"),
- new File(targetDir, "subDir3"));
- }
-
- public void testExtractZipSafely_badZip_fileOutsideTarget() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
- addZipEntry(zipOutputStream, "../one");
- }
- doExtractZipFails(baos);
- }
-
- public void testExtractZipSafely_badZip_dirOutsideTarget() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
- addZipEntry(zipOutputStream, "../one/");
- }
- doExtractZipFails(baos);
- }
-
- private void doExtractZipFails(ByteArrayOutputStream baos) {
- File dir = createTempDir();
- File targetDir = new File(dir, "target");
- TestInputStream inputStream = new TestInputStream(
- new ByteArrayInputStream(baos.toByteArray()));
- try {
- ConfigBundle.extractZipSafely(inputStream, targetDir, true /* makeWorldReadable */);
- fail();
- } catch (IOException expected) {
- }
- inputStream.assertClosed();
- }
-
- private static void addZipEntry(ZipOutputStream zipOutputStream, String name)
- throws IOException {
- ZipEntry zipEntry = new ZipEntry(name);
- zipOutputStream.putNextEntry(zipEntry);
- if (!zipEntry.isDirectory()) {
- zipOutputStream.write('a');
- }
- }
-
- private File createTempDir() {
- final String tempPrefix = getClass().getSimpleName();
- File tempDir = IoUtils.createTemporaryDirectory(tempPrefix);
- testFiles.add(tempDir);
- return tempDir;
- }
-
- private static void assertFilesExist(File... files) {
- for (File f : files) {
- assertTrue(f + " file expected to exist", f.exists() && f.isFile());
- }
- }
-
- private static void assertDirsExist(File... dirs) {
- for (File dir : dirs) {
- assertTrue(dir + " directory expected to exist", dir.exists() && dir.isDirectory());
- }
- }
-
- private static class TestInputStream extends FilterInputStream {
-
- private boolean closed;
-
- public TestInputStream(InputStream in) {
- super(in);
- }
-
- @Override
- public void close() throws IOException {
- closed = true;
- super.close();
- }
-
- public void assertClosed() {
- assertTrue(closed);
- }
- }
-}
diff --git a/tzdata/update/src/test/libcore/tzdata/update/FileUtilsTest.java b/tzdata/update/src/test/libcore/tzdata/update/FileUtilsTest.java
deleted file mode 100644
index d002820..0000000
--- a/tzdata/update/src/test/libcore/tzdata/update/FileUtilsTest.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.tzdata.update;
-
-import junit.framework.TestCase;
-
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.StructStat;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import libcore.io.IoUtils;
-import libcore.io.Libcore;
-
-/**
- * Tests for {@link FileUtils}.
- */
-public class FileUtilsTest extends TestCase {
-
- private List<File> testFiles = new ArrayList<>();
-
- @Override
- public void tearDown() throws Exception {
- // Delete in reverse order
- Collections.reverse(testFiles);
- for (File tempFile : testFiles) {
- tempFile.delete();
- }
- super.tearDown();
- }
-
- public void testCalculateChecksum() throws Exception {
- final String content = "Content";
- File file1 = createTextFile(content);
- File file2 = createTextFile(content);
- File file3 = createTextFile(content + "!");
-
- long file1CheckSum = FileUtils.calculateChecksum(file1);
- long file2CheckSum = FileUtils.calculateChecksum(file2);
- long file3Checksum = FileUtils.calculateChecksum(file3);
-
- assertEquals(file1CheckSum, file2CheckSum);
- assertTrue(file1CheckSum != file3Checksum);
- }
-
- public void testDeleteRecursive() throws Exception {
- File dir = createTempDir();
- File file1 = createRegularFile(dir, "file1");
- File file2 = createRegularFile(dir, "file2");
- File symLink1 = createSymlink(file1, dir, "symLink1");
- File subDir = createDir(dir, "subDir");
- File file3 = createRegularFile(subDir, "subFile1");
- File file4 = createRegularFile(subDir, "subFile2");
- File symLink2 = createSymlink(file1, dir, "symLink2");
-
- File otherDir = createTempDir();
- File otherFile = createRegularFile(otherDir, "kept");
-
- File linkToOtherDir = createSymlink(otherDir, subDir, "linkToOtherDir");
- File linkToOtherFile = createSymlink(otherFile, subDir, "linkToOtherFile");
-
- File[] filesToDelete = { dir, file1, file2, symLink1, subDir, file3, file4, symLink2,
- linkToOtherDir, linkToOtherFile };
- File[] filesToKeep = { otherDir, otherFile };
- assertFilesExist(filesToDelete);
- assertFilesExist(filesToKeep);
-
- FileUtils.deleteRecursive(dir);
- assertFilesDoNotExist(filesToDelete);
- assertFilesExist(filesToKeep);
- }
-
- public void testIsSymlink() throws Exception {
- File dir = createTempDir();
- File subDir = createDir(dir, "subDir");
- File fileInSubDir = createRegularFile(subDir, "fileInSubDir");
- File normalFile = createRegularFile(dir, "normalFile");
- File symlinkToDir = createSymlink(subDir, dir, "symlinkToDir");
- File symlinkToFile = createSymlink(fileInSubDir, dir, "symlinkToFile");
- File symlinkToFileInSubDir = createSymlink(fileInSubDir, dir, "symlinkToFileInSubDir");
- File normalFileViaSymlink = new File(symlinkToDir, "normalFile");
-
- assertFalse(FileUtils.isSymlink(dir));
- assertFalse(FileUtils.isSymlink(subDir));
- assertFalse(FileUtils.isSymlink(fileInSubDir));
- assertFalse(FileUtils.isSymlink(normalFile));
- assertTrue(FileUtils.isSymlink(symlinkToDir));
- assertTrue(FileUtils.isSymlink(symlinkToFile));
- assertTrue(FileUtils.isSymlink(symlinkToFileInSubDir));
- assertFalse(FileUtils.isSymlink(normalFileViaSymlink));
- }
-
- public void testCreateSubFile() throws Exception {
- File dir1 = createTempDir().getCanonicalFile();
-
- File actualSubFile = FileUtils.createSubFile(dir1, "file");
- assertEquals(new File(dir1, "file"), actualSubFile);
-
- File existingSubFile = createRegularFile(dir1, "file");
- actualSubFile = FileUtils.createSubFile(dir1, "file");
- assertEquals(existingSubFile, actualSubFile);
-
- File existingSubDir = createDir(dir1, "subdir");
- actualSubFile = FileUtils.createSubFile(dir1, "subdir");
- assertEquals(existingSubDir, actualSubFile);
-
- assertCreateSubFileThrows(dir1, "../file");
- assertCreateSubFileThrows(dir1, "../../file");
- assertCreateSubFileThrows(dir1, "../otherdir/file");
-
- File dir2 = createTempDir().getCanonicalFile();
- createSymlink(dir2, dir1, "symlinkToDir2");
- assertCreateSubFileThrows(dir1, "symlinkToDir2");
-
- assertCreateSubFileThrows(dir1, "symlinkToDir2/fileInSymlinkedDir");
-
- createRegularFile(dir1, "symlinkToDir2/fileInSymlinkedDir");
- assertCreateSubFileThrows(dir1, "symlinkToDir2/fileInSymlinkedDir");
- }
-
- public void testEnsureDirectoryExists() throws Exception {
- File dir = createTempDir();
-
- File exists = new File(dir, "exists");
- assertTrue(exists.mkdir());
- assertTrue(exists.setReadable(true /* readable */, true /* ownerOnly */));
- assertTrue(exists.setExecutable(true /* readable */, true /* ownerOnly */));
- FileUtils.ensureDirectoriesExist(exists, true /* makeWorldReadable */);
- assertDirExistsAndIsAccessible(exists, false /* requireWorldReadable */);
-
- File subDir = new File(dir, "subDir");
- assertFalse(subDir.exists());
- FileUtils.ensureDirectoriesExist(subDir, true /* makeWorldReadable */);
- assertDirExistsAndIsAccessible(subDir, true /* requireWorldReadable */);
-
- File one = new File(dir, "one");
- File two = new File(one, "two");
- File three = new File(two, "three");
- FileUtils.ensureDirectoriesExist(three, true /* makeWorldReadable */);
- assertDirExistsAndIsAccessible(one, true /* requireWorldReadable */);
- assertDirExistsAndIsAccessible(two, true /* requireWorldReadable */);
- assertDirExistsAndIsAccessible(three, true /* requireWorldReadable */);
- }
-
- public void testEnsureDirectoriesExist_noPermissions() throws Exception {
- File dir = createTempDir();
- assertDirExistsAndIsAccessible(dir, false /* requireWorldReadable */);
-
- File unreadableSubDir = new File(dir, "unreadableSubDir");
- assertTrue(unreadableSubDir.mkdir());
- assertTrue(unreadableSubDir.setReadable(false /* readable */, true /* ownerOnly */));
- assertTrue(unreadableSubDir.setExecutable(false /* readable */, true /* ownerOnly */));
-
- File toCreate = new File(unreadableSubDir, "toCreate");
- try {
- FileUtils.ensureDirectoriesExist(toCreate, true /* makeWorldReadable */);
- fail();
- } catch (IOException expected) {
- }
- assertDirExistsAndIsAccessible(dir, false /* requireWorldReadable */);
- assertFalse(unreadableSubDir.canRead() && unreadableSubDir.canExecute());
- assertFalse(toCreate.exists());
- }
-
- public void testEnsureFileDoesNotExist() throws Exception {
- File dir = createTempDir();
-
- FileUtils.ensureFileDoesNotExist(new File(dir, "doesNotExist"));
-
- File exists1 = createRegularFile(dir, "exists1");
- assertTrue(exists1.exists());
- FileUtils.ensureFileDoesNotExist(exists1);
- assertFalse(exists1.exists());
-
- exists1 = createRegularFile(dir, "exists1");
- File symlink = createSymlink(exists1, dir, "symlinkToFile");
- assertTrue(symlink.exists());
- FileUtils.ensureFileDoesNotExist(symlink);
- assertFalse(symlink.exists());
- assertTrue(exists1.exists());
-
- // Only files and symlinks supported. We do not delete directories.
- File emptyDir = createTempDir();
- try {
- FileUtils.ensureFileDoesNotExist(emptyDir);
- fail();
- } catch (IOException expected) {
- }
- assertTrue(emptyDir.exists());
- }
-
- // This test does not pass when run as root because root can do anything even if the permissions
- // don't allow it.
- public void testEnsureFileDoesNotExist_noPermission() throws Exception {
- File dir = createTempDir();
-
- File protectedDir = createDir(dir, "protected");
- File undeletable = createRegularFile(protectedDir, "undeletable");
- assertTrue(protectedDir.setWritable(false));
- assertTrue(undeletable.exists());
- try {
- FileUtils.ensureFileDoesNotExist(undeletable);
- fail();
- } catch (IOException expected) {
- } finally {
- assertTrue(protectedDir.setWritable(true)); // Reset for clean-up
- }
- assertTrue(undeletable.exists());
- }
-
- public void testCheckFilesExist() throws Exception {
- File dir = createTempDir();
- createRegularFile(dir, "exists1");
- File subDir = createDir(dir, "subDir");
- createRegularFile(subDir, "exists2");
- assertTrue(FileUtils.filesExist(dir, "exists1", "subDir/exists2"));
- assertFalse(FileUtils.filesExist(dir, "doesNotExist"));
- assertFalse(FileUtils.filesExist(dir, "subDir/doesNotExist"));
- }
-
- public void testReadLines() throws Exception {
- File file = createTextFile("One\nTwo\nThree\n");
-
- List<String> lines = FileUtils.readLines(file);
- assertEquals(3, lines.size());
- assertEquals(lines, Arrays.asList("One", "Two", "Three"));
- }
-
- private File createTextFile(String contents) throws IOException {
- File file = File.createTempFile(getClass().getSimpleName(), ".txt");
- try (FileOutputStream fos = new FileOutputStream(file)) {
- BufferedWriter writer = new BufferedWriter(
- new OutputStreamWriter(fos, StandardCharsets.UTF_8));
- writer.write(contents);
- writer.close();
- }
- return file;
- }
-
- private File createSymlink(File file, File symlinkDir, String symlinkName) throws Exception {
- assertTrue(file.exists());
-
- File symlink = new File(symlinkDir, symlinkName);
- Os.symlink(file.getAbsolutePath(), symlink.getAbsolutePath());
- testFiles.add(symlink);
- return symlink;
- }
-
- private static void assertCreateSubFileThrows(File parentDir, String name) {
- try {
- FileUtils.createSubFile(parentDir, name);
- fail();
- } catch (IOException expected) {
- assertTrue(expected.getMessage().contains("must exist beneath"));
- }
- }
-
- private static void assertFilesDoNotExist(File... files) {
- for (File f : files) {
- assertFalse(f + " unexpectedly exists", f.exists());
- }
- }
-
- private static void assertFilesExist(File... files) {
- for (File f : files) {
- assertTrue(f + " expected to exist", f.exists());
- }
- }
-
- private static void assertDirExistsAndIsAccessible(File dir, boolean requireWorldReadable)
- throws Exception {
- assertTrue(dir.exists() && dir.isDirectory() && dir.canRead() && dir.canExecute());
-
- String path = dir.getCanonicalPath();
- StructStat sb = Libcore.os.stat(path);
- int mask = OsConstants.S_IXUSR | OsConstants.S_IRUSR;
- if (requireWorldReadable) {
- mask = mask | OsConstants.S_IXGRP | OsConstants.S_IRGRP
- | OsConstants.S_IXOTH | OsConstants.S_IROTH;
- }
- assertTrue("Permission mask required: " + Integer.toOctalString(mask),
- (sb.st_mode & mask) == mask);
- }
-
- private File createTempDir() {
- final String tempPrefix = getClass().getSimpleName();
- File tempDir = IoUtils.createTemporaryDirectory(tempPrefix);
- testFiles.add(tempDir);
- return tempDir;
- }
-
- private File createDir(File parentDir, String name) {
- File dir = new File(parentDir, name);
- assertTrue(dir.mkdir());
- testFiles.add(dir);
- return dir;
- }
-
- private File createRegularFile(File dir, String name) throws Exception {
- File file = new File(dir, name);
- try (FileOutputStream fos = new FileOutputStream(file)) {
- fos.write("Hello".getBytes());
- }
- testFiles.add(file);
- return file;
- }
-}
diff --git a/tzdata/update/src/test/libcore/tzdata/update/TzDataBundleInstallerTest.java b/tzdata/update/src/test/libcore/tzdata/update/TzDataBundleInstallerTest.java
deleted file mode 100644
index 1825bb3..0000000
--- a/tzdata/update/src/test/libcore/tzdata/update/TzDataBundleInstallerTest.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.tzdata.update;
-
-import junit.framework.TestCase;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import libcore.tzdata.update.tools.TzDataBundleBuilder;
-
-/**
- * Tests for {@link libcore.tzdata.update.TzDataBundleInstaller}.
- */
-public class TzDataBundleInstallerTest extends TestCase {
-
- private static final File SYSTEM_ZONE_INFO_FILE = new File("/system/usr/share/zoneinfo/tzdata");
-
- private TzDataBundleInstaller installer;
- private File tempDir;
- private File testInstallDir;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- tempDir = createDirectory("tempDir");
- testInstallDir = createDirectory("testInstall");
- installer = new TzDataBundleInstaller("TzDataBundleInstallerTest", testInstallDir);
- }
-
- private static File createDirectory(String prefix) throws IOException {
- File dir = File.createTempFile(prefix, "");
- assertTrue(dir.delete());
- assertTrue(dir.mkdir());
- return dir;
- }
-
- @Override
- public void tearDown() throws Exception {
- if (testInstallDir.exists()) {
- FileUtils.deleteRecursive(testInstallDir);
- }
- if (tempDir.exists()) {
- FileUtils.deleteRecursive(tempDir);
- }
- super.tearDown();
- }
-
- /** Tests the first update on a device */
- public void testSuccessfulFirstUpdate() throws Exception {
- ConfigBundle tzData = createValidTzDataBundle("2030a");
-
- assertTrue(install(tzData));
- assertTzDataInstalled(tzData);
- }
-
- /**
- * Tests an update on a device when there is a prior update already applied.
- */
- public void testSuccessfulFollowOnUpdate() throws Exception {
- ConfigBundle tzData1 = createValidTzDataBundle("2030a");
- assertTrue(install(tzData1));
- assertTzDataInstalled(tzData1);
-
- ConfigBundle tzData2 = createValidTzDataBundle("2030b");
- assertTrue(install(tzData2));
- assertTzDataInstalled(tzData2);
- }
-
-
- /** Tests that a bundle with a missing file will not update the content. */
- public void testMissingRequiredBundleFile() throws Exception {
- ConfigBundle installedConfigBundle = createValidTzDataBundle("2030a");
- assertTrue(install(installedConfigBundle));
- assertTzDataInstalled(installedConfigBundle);
-
- ConfigBundle incompleteUpdate =
- createValidTzDataBundleBuilder("2030b").clearBionicTzData().buildUnvalidated();
- assertFalse(install(incompleteUpdate));
- assertTzDataInstalled(installedConfigBundle);
- }
-
- /**
- * Tests that an update will be unpacked even if there is a partial update from a previous run.
- */
- public void testInstallWithWorkingDir() throws Exception {
- File workingDir = new File(testInstallDir, TzDataBundleInstaller.WORKING_DIR_NAME);
- assertTrue(workingDir.mkdir());
- createFile(new File(workingDir, "myFile"));
-
- ConfigBundle tzData = createValidTzDataBundle("2030a");
- assertTrue(install(tzData));
- assertTzDataInstalled(tzData);
- }
-
- /**
- * Tests that a bundle with a checksum entry that references a missing file will not update the
- * content.
- */
- public void testChecksumBundleEntry_fileMissing() throws Exception {
- ConfigBundle badUpdate =
- createValidTzDataBundleBuilder("2030b")
- .addChecksum("/fileDoesNotExist", 1234)
- .build();
- assertFalse(install(badUpdate));
- assertNoContentInstalled();
- }
-
- /**
- * Tests that a bundle with a checksum entry with a bad checksum will not update the
- * content.
- */
- public void testChecksumBundleEntry_incorrectChecksum() throws Exception {
- File fileToChecksum = SYSTEM_ZONE_INFO_FILE;
- long badChecksum = FileUtils.calculateChecksum(fileToChecksum) + 1;
- ConfigBundle badUpdate =
- createValidTzDataBundleBuilder("2030b")
- .clearChecksumEntries()
- .addChecksum(fileToChecksum.getPath(), badChecksum)
- .build();
- assertFalse(install(badUpdate));
- assertNoContentInstalled();
- }
-
- private boolean install(ConfigBundle configBundle) throws Exception {
- return installer.install(configBundle.getBundleBytes());
- }
-
- private ConfigBundle createValidTzDataBundle(String tzDataVersion)
- throws IOException {
- return createValidTzDataBundleBuilder(tzDataVersion).build();
- }
-
- private TzDataBundleBuilder createValidTzDataBundleBuilder(String tzDataVersion)
- throws IOException {
-
- // The file to include in the installation-time checksum check.
- File fileToChecksum = SYSTEM_ZONE_INFO_FILE;
- long checksum = FileUtils.calculateChecksum(fileToChecksum);
-
- File bionicTzData = new File(tempDir, "zoneinfo");
- createFile(bionicTzData);
-
- File icuData = new File(tempDir, "icudata");
- createFile(icuData);
-
- return new TzDataBundleBuilder()
- .addChecksum(fileToChecksum.getPath(), checksum)
- .setTzDataVersion(tzDataVersion)
- .addBionicTzData(bionicTzData)
- .addIcuTzData(icuData);
- }
-
- private void assertTzDataInstalled(ConfigBundle expectedTzData) throws Exception {
- assertTrue(testInstallDir.exists());
-
- File currentTzDataDir = new File(testInstallDir, TzDataBundleInstaller.CURRENT_TZ_DATA_DIR_NAME);
- assertTrue(currentTzDataDir.exists());
-
- File checksumFile = new File(currentTzDataDir, ConfigBundle.CHECKSUMS_FILE_NAME);
- assertTrue(checksumFile.exists());
-
- File versionFile = new File(currentTzDataDir,
- ConfigBundle.TZ_DATA_VERSION_FILE_NAME);
- assertTrue(versionFile.exists());
-
- File bionicFile = new File(currentTzDataDir, ConfigBundle.ZONEINFO_FILE_NAME);
- assertTrue(bionicFile.exists());
-
- File icuFile = new File(currentTzDataDir, ConfigBundle.ICU_DATA_FILE_NAME);
- assertTrue(icuFile.exists());
-
- // Also check no working directory is left lying around.
- File workingDir = new File(testInstallDir, TzDataBundleInstaller.WORKING_DIR_NAME);
- assertFalse(workingDir.exists());
- }
-
- private void assertNoContentInstalled() {
- File currentTzDataDir = new File(testInstallDir, TzDataBundleInstaller.CURRENT_TZ_DATA_DIR_NAME);
- assertFalse(currentTzDataDir.exists());
-
- // Also check no working directories are left lying around.
- File workingDir = new File(testInstallDir, TzDataBundleInstaller.WORKING_DIR_NAME);
- assertFalse(workingDir.exists());
-
- File oldDataDir = new File(testInstallDir, TzDataBundleInstaller.OLD_TZ_DATA_DIR_NAME);
- assertFalse(oldDataDir.exists());
- }
-
- private static void createFile(File file) {
- try (FileOutputStream fos = new FileOutputStream(file)) {
- fos.write('a');
- } catch (IOException e) {
- fail(e.getMessage());
- }
- }
-}
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java b/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
index ae4af45..4848db7 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
@@ -30,12 +30,14 @@
* The major distro format version supported by this device.
* Increment this for non-backwards compatible changes to the distro format. Reset the minor
* version to 1 when doing so.
+ * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
*/
public static final int CURRENT_FORMAT_MAJOR_VERSION = 1;
/**
* The minor distro format version supported by this device. Increment this for
* backwards-compatible changes to the distro format.
+ * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
*/
public static final int CURRENT_FORMAT_MINOR_VERSION = 1;
@@ -122,10 +124,8 @@
}
public static boolean isCompatibleWithThisDevice(DistroVersion distroVersion) {
- return (DistroVersion.CURRENT_FORMAT_MAJOR_VERSION
- == distroVersion.formatMajorVersion)
- && (DistroVersion.CURRENT_FORMAT_MINOR_VERSION
- <= distroVersion.formatMinorVersion);
+ return (CURRENT_FORMAT_MAJOR_VERSION == distroVersion.formatMajorVersion)
+ && (CURRENT_FORMAT_MINOR_VERSION <= distroVersion.formatMinorVersion);
}
@Override
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
index c04b4cb..04e3f2a 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
@@ -40,6 +40,7 @@
/**
* The name of the file inside the distro containing the distro version information.
* The content is ASCII bytes representing a set of version numbers. See {@link DistroVersion}.
+ * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
*/
public static final String DISTRO_VERSION_FILE_NAME = "distro_version";
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
similarity index 98%
rename from tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java
rename to tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
index d08bb1d..1b56ebf 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
@@ -26,8 +26,7 @@
* A distro-validation / extraction class. Separate from the services code that uses it for easier
* testing. This class is not thread-safe: callers are expected to handle mutual exclusion.
*/
-// TODO(nfuller) Rename to TimeZoneDistroInstaller
-public final class TimeZoneBundleInstaller {
+public final class TimeZoneDistroInstaller {
/** {@link #installWithErrorCode(byte[])} result code: Success. */
public final static int INSTALL_SUCCESS = 0;
/** {@link #installWithErrorCode(byte[])} result code: Distro corrupt. */
@@ -49,7 +48,7 @@
private final File currentTzDataDir;
private final File workingDir;
- public TimeZoneBundleInstaller(String logTag, File systemTzDataFile, File installDir) {
+ public TimeZoneDistroInstaller(String logTag, File systemTzDataFile, File installDir) {
this.logTag = logTag;
this.systemTzDataFile = systemTzDataFile;
oldTzDataDir = new File(installDir, OLD_TZ_DATA_DIR_NAME);
diff --git a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
similarity index 92%
rename from tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java
rename to tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
index 088e7cb..6ae0e56 100644
--- a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java
+++ b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
@@ -34,9 +34,9 @@
import libcore.tzdata.update2.tools.TimeZoneDistroBuilder;
/**
- * Tests for {@link TimeZoneBundleInstaller}.
+ * Tests for {@link TimeZoneDistroInstaller}.
*/
-public class TimeZoneBundleInstallerTest extends TestCase {
+public class TimeZoneDistroInstallerTest extends TestCase {
// OLDER_RULES_VERSION < SYSTEM_RULES_VERSION < NEW_RULES_VERSION < NEWER_RULES_VERSION
private static final String OLDER_RULES_VERSION = "2030a";
@@ -44,7 +44,7 @@
private static final String NEW_RULES_VERSION = "2030c";
private static final String NEWER_RULES_VERSION = "2030d";
- private TimeZoneBundleInstaller installer;
+ private TimeZoneDistroInstaller installer;
private File tempDir;
private File testInstallDir;
private File testSystemTzDataDir;
@@ -61,8 +61,8 @@
byte[] systemTzDataBytes = createTzData(SYSTEM_RULES_VERSION);
createFile(testSystemTzDataFile, systemTzDataBytes);
- installer = new TimeZoneBundleInstaller(
- "TimeZoneBundleInstallerTest", testSystemTzDataFile, testInstallDir);
+ installer = new TimeZoneDistroInstaller(
+ "TimeZoneDistroInstallerTest", testSystemTzDataFile, testInstallDir);
}
private static File createDirectory(String prefix) throws Exception {
@@ -89,8 +89,8 @@
/** Tests the an update on a device will fail if the /system tzdata file cannot be found. */
public void testInstall_badSystemFile() throws Exception {
File doesNotExist = new File(testSystemTzDataDir, "doesNotExist");
- TimeZoneBundleInstaller brokenSystemInstaller = new TimeZoneBundleInstaller(
- "TimeZoneBundleInstallerTest", doesNotExist, testInstallDir);
+ TimeZoneDistroInstaller brokenSystemInstaller = new TimeZoneDistroInstaller(
+ "TimeZoneDistroInstallerTest", doesNotExist, testInstallDir);
TimeZoneDistro tzData = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
try {
@@ -106,7 +106,7 @@
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
@@ -117,7 +117,7 @@
public void testInstall_successfulFirstUpdate_sameVersionAsSystem() throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(SYSTEM_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
@@ -128,7 +128,7 @@
public void testInstall_unsuccessfulFirstUpdate_olderVersionThanSystem() throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_RULES_TOO_OLD,
+ TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -139,19 +139,19 @@
public void testInstall_successfulFollowOnUpdate_newerVersion() throws Exception {
TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro1.getBytes()));
assertDistroInstalled(distro1);
TimeZoneDistro distro2 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro2.getBytes()));
assertDistroInstalled(distro2);
TimeZoneDistro distro3 = createValidTimeZoneDistro(NEWER_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro3.getBytes()));
assertDistroInstalled(distro3);
}
@@ -163,13 +163,13 @@
public void testInstall_unsuccessfulFollowOnUpdate_olderVersion() throws Exception {
TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro1.getBytes()));
assertDistroInstalled(distro1);
TimeZoneDistro distro2 = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_RULES_TOO_OLD,
+ TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
installer.installWithErrorCode(distro2.getBytes()));
assertDistroInstalled(distro1);
}
@@ -178,7 +178,7 @@
public void testInstall_missingTzDataFile() throws Exception {
TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(installedDistro.getBytes()));
assertDistroInstalled(installedDistro);
@@ -187,7 +187,7 @@
.clearTzDataForTests()
.buildUnvalidated();
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(incompleteDistro.getBytes()));
assertDistroInstalled(installedDistro);
}
@@ -196,7 +196,7 @@
public void testInstall_missingIcuFile() throws Exception {
TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(installedDistro.getBytes()));
assertDistroInstalled(installedDistro);
@@ -205,7 +205,7 @@
.clearIcuDataForTests()
.buildUnvalidated();
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(incompleteDistro.getBytes()));
assertDistroInstalled(installedDistro);
}
@@ -220,7 +220,7 @@
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_SUCCESS,
+ TimeZoneDistroInstaller.INSTALL_SUCCESS,
installer.installWithErrorCode(distro.getBytes()));
assertDistroInstalled(distro);
}
@@ -234,7 +234,7 @@
.clearVersionForTests()
.buildUnvalidated();
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -248,7 +248,7 @@
.replaceFormatVersionForTests(2, 1)
.buildUnvalidated();
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -265,7 +265,7 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidFormatVersionBytes);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -281,7 +281,7 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRevisionBytes);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}
@@ -297,7 +297,7 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRulesVersionBytes);
assertEquals(
- TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+ TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
installer.installWithErrorCode(distro.getBytes()));
assertNoContentInstalled();
}