am 8ebfe8fb: Delete test that depends on an external server.
* commit '8ebfe8fb75f796ae77162787e8a44f5e03b847e2':
Delete test that depends on an external server.
diff --git a/CaCerts.mk b/CaCerts.mk
index cdced8f..980d0fb 100644
--- a/CaCerts.mk
+++ b/CaCerts.mk
@@ -30,6 +30,7 @@
define include-prebuilt-with-destination-directory
include $$(CLEAR_VARS)
LOCAL_MODULE := $(1)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/CaCerts.mk
LOCAL_MODULE_STEM := $(notdir $(2))
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index 18fa870..59ac2e1 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -82,6 +82,7 @@
LOCAL_NO_EMMA_COMPILE := true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := core
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
include $(BUILD_JAVA_LIBRARY)
@@ -98,6 +99,7 @@
LOCAL_JAVACFLAGS := $(local_javac_flags)
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := core-tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
LOCAL_NO_EMMA_INSTRUMENT := true
LOCAL_NO_EMMA_COMPILE := true
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -140,6 +142,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := core-hostdex
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
include $(BUILD_HOST_JAVA_LIBRARY)
@@ -151,8 +154,9 @@
LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-hostdex core-junit-hostdex
LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc-host mockwebserver-hostdex
LOCAL_JAVACFLAGS := $(local_javac_flags)
- LOCAL_MODULE_TAGS := tests
+ LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := core-tests-hostdex
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
LOCAL_NO_EMMA_INSTRUMENT := true
LOCAL_NO_EMMA_COMPILE := true
LOCAL_BUILD_HOST_DEX := true
@@ -190,6 +194,7 @@
LOCAL_MODULE_CLASS:=JAVA_LIBRARIES
LOCAL_MODULE := libcore
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
LOCAL_DROIDDOC_OPTIONS:= \
-offlinemode \
diff --git a/NativeCode.mk b/NativeCode.mk
index 2a222b1..1aab901 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -53,6 +53,7 @@
# set up.
include $(CLEAR_VARS)
LOCAL_MODULE := $(core_magic_local_target)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
core_src_files :=
# Include the sub.mk files.
@@ -65,9 +66,6 @@
core_c_includes := $(sort libcore/include $(LOCAL_C_INCLUDES) $(JNI_H_INCLUDE))
core_shared_libraries := $(sort $(LOCAL_SHARED_LIBRARIES))
core_static_libraries := $(sort $(LOCAL_STATIC_LIBRARIES))
-core_cflags := -fvisibility=hidden
-core_cflags += '-DGCC_HIDDEN=__attribute__((visibility("hidden")))'
-core_cppflags := -fvisibility-inlines-hidden
#
@@ -91,6 +89,7 @@
LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libjavacore
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
LOCAL_SHARED_LIBRARIES += libstlport
@@ -111,6 +110,7 @@
LOCAL_LDLIBS += -ldl -lpthread
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libjavacore
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libexpat libicuuc libicui18n libssl libcrypto libz-host
LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/ThirdPartyProject.prop b/ThirdPartyProject.prop
deleted file mode 100644
index 9f828fa..0000000
--- a/ThirdPartyProject.prop
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2010 Google Inc. All Rights Reserved.
-#Fri Jul 16 10:03:08 PDT 2010
-currentVersion=5.0M10
-version=Unknown
-isNative=false
-name=apache_harmony
-keywords=apache harmony
-onDevice=true
-homepage=http\://harmony.apache.org/
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index ab24f0b..62ec5e3 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -28,6 +28,9 @@
/** originally specified path (just used for {@code toString()}) */
private final String originalPath;
+ /** originally specified library path (just used for {@code toString()}) */
+ private final String originalLibraryPath;
+
/** structured lists of path elements */
private final DexPathList pathList;
@@ -49,6 +52,7 @@
super(parent);
this.originalPath = dexPath;
+ this.originalLibraryPath = libraryPath;
this.pathList =
new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
@@ -58,7 +62,7 @@
Class clazz = pathList.findClass(name);
if (clazz == null) {
- throw new ClassNotFoundException(name);
+ throw new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + originalPath);
}
return clazz;
@@ -123,6 +127,7 @@
@Override
public String toString() {
- return getClass().getName() + "[" + originalPath + "]";
+ return getClass().getName()
+ + "[dexPath=" + originalPath + ",libraryPath=" + originalLibraryPath + "]";
}
}
diff --git a/dalvik/src/main/java/dalvik/system/CloseGuard.java b/dalvik/src/main/java/dalvik/system/CloseGuard.java
index 136be2f..df36867 100644
--- a/dalvik/src/main/java/dalvik/system/CloseGuard.java
+++ b/dalvik/src/main/java/dalvik/system/CloseGuard.java
@@ -197,7 +197,7 @@
/**
* If CloseGuard is enabled, logs a warning if the caller did not
* properly cleanup by calling an explicit close method
- * before finalization. If CloseGuard is disable, no action is
+ * before finalization. If CloseGuard is disabled, no action is
* performed.
*/
public void warnIfOpen() {
@@ -223,7 +223,7 @@
* Default Reporter which reports CloseGuard violations to the log.
*/
private static final class DefaultReporter implements Reporter {
- public void report (String message, Throwable allocationSite) {
+ @Override public void report (String message, Throwable allocationSite) {
System.logW(message, allocationSite);
}
}
diff --git a/dalvik/src/main/java/dalvik/system/VMRuntime.java b/dalvik/src/main/java/dalvik/system/VMRuntime.java
index c37290d..71098be 100644
--- a/dalvik/src/main/java/dalvik/system/VMRuntime.java
+++ b/dalvik/src/main/java/dalvik/system/VMRuntime.java
@@ -91,7 +91,7 @@
* @throws IllegalArgumentException if newTarget is <= 0.0 or >= 1.0
*/
public float setTargetHeapUtilization(float newTarget) {
- if (newTarget <= 0.0 || newTarget >= 1.0) {
+ if (newTarget <= 0.0f || newTarget >= 1.0f) {
throw new IllegalArgumentException(newTarget +
" out of range (0,1)");
}
diff --git a/dalvik/src/main/java/dalvik/system/Zygote.java b/dalvik/src/main/java/dalvik/system/Zygote.java
index 28c9912..ec114ed 100644
--- a/dalvik/src/main/java/dalvik/system/Zygote.java
+++ b/dalvik/src/main/java/dalvik/system/Zygote.java
@@ -107,20 +107,23 @@
* dimension having a length of 3 and representing
* (resource, rlim_cur, rlim_max). These are set via the posix
* setrlimit(2) call.
+ * @param seInfo null-ok a string specifying SEAndroid information for
+ * the new process.
+ * @param niceName null-ok a string specifying the process name.
*
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids,
- int debugFlags, int[][] rlimits) {
+ int debugFlags, int[][] rlimits, String seInfo, String niceName) {
preFork();
- int pid = nativeForkAndSpecialize(uid, gid, gids, debugFlags, rlimits);
+ int pid = nativeForkAndSpecialize(uid, gid, gids, debugFlags, rlimits, seInfo, niceName);
postFork();
return pid;
}
native public static int nativeForkAndSpecialize(int uid, int gid,
- int[] gids, int debugFlags, int[][] rlimits);
+ int[] gids, int debugFlags, int[][] rlimits, String seInfo, String niceName);
/**
* Forks a new VM instance.
@@ -130,7 +133,7 @@
public static int forkAndSpecialize(int uid, int gid, int[] gids,
boolean enableDebugger, int[][] rlimits) {
int debugFlags = enableDebugger ? DEBUG_ENABLE_DEBUGGER : 0;
- return forkAndSpecialize(uid, gid, gids, debugFlags, rlimits);
+ return forkAndSpecialize(uid, gid, gids, debugFlags, rlimits, null, null);
}
/**
@@ -175,7 +178,7 @@
public static int forkSystemServer(int uid, int gid, int[] gids,
boolean enableDebugger, int[][] rlimits) {
int debugFlags = enableDebugger ? DEBUG_ENABLE_DEBUGGER : 0;
- return forkAndSpecialize(uid, gid, gids, debugFlags, rlimits);
+ return forkAndSpecialize(uid, gid, gids, debugFlags, rlimits, null, null);
}
native public static int nativeForkSystemServer(int uid, int gid,
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index fe110ea..972f1ef 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -89,12 +89,6 @@
substring: "java.net.URISyntaxException"
},
{
- description: "Test regression",
- name: "libcore.javax.crypto.spec.KeyFactoryTestDSA#testKeyFactory",
- substring: "not implemented yet",
- bug: 3286592
-},
-{
description: "KxmlParser doesn't expose DTD text",
name: "libcore.xml.KxmlPullParserDtdTest#testDoctypeWithNextToken",
bug: 3241492
diff --git a/include/ScopedLocalRef.h b/include/ScopedLocalRef.h
index 84ee11a..71d5776 100644
--- a/include/ScopedLocalRef.h
+++ b/include/ScopedLocalRef.h
@@ -17,28 +17,36 @@
#ifndef SCOPED_LOCAL_REF_H_included
#define SCOPED_LOCAL_REF_H_included
-#include "JNIHelp.h"
+#include "jni.h"
+
+#include <stddef.h>
// A smart pointer that deletes a JNI local reference when it goes out of scope.
template<typename T>
class ScopedLocalRef {
public:
- ScopedLocalRef(JNIEnv* env, T localRef)
- : mEnv(env), mLocalRef(localRef)
- {
+ ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
}
~ScopedLocalRef() {
reset();
}
- void reset() {
- if (mLocalRef != NULL) {
- mEnv->DeleteLocalRef(mLocalRef);
- mLocalRef = NULL;
+ void reset(T ptr = NULL) {
+ if (ptr != mLocalRef) {
+ if (mLocalRef != NULL) {
+ mEnv->DeleteLocalRef(mLocalRef);
+ }
+ mLocalRef = ptr;
}
}
+ T release() __attribute__((warn_unused_result)) {
+ T localRef = mLocalRef;
+ mLocalRef = NULL;
+ return localRef;
+ }
+
T get() const {
return mLocalRef;
}
diff --git a/luni/src/main/files/cacerts/1e1eab7c.0 b/luni/src/main/files/cacerts/1e1eab7c.0
new file mode 100644
index 0000000..a7cf5f9
--- /dev/null
+++ b/luni/src/main/files/cacerts/1e1eab7c.0
@@ -0,0 +1,80 @@
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
+KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
+BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
+YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1
+OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
+aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
+ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN
+8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/
+RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4
+hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5
+ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM
+EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj
+QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1
+A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy
+WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ
+1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30
+6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT
+91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
+e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p
+TpPDpFQUWw==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=DE, O=T-Systems Enterprise Services GmbH, OU=T-Systems Trust Center, CN=T-TeleSec GlobalRoot Class 3
+ Validity
+ Not Before: Oct 1 10:29:56 2008 GMT
+ Not After : Oct 1 23:59:59 2033 GMT
+ Subject: C=DE, O=T-Systems Enterprise Services GmbH, OU=T-Systems Trust Center, CN=T-TeleSec GlobalRoot Class 3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (2048 bit)
+ Modulus (2048 bit):
+ 00:bd:75:93:f0:62:22:6f:24:ae:e0:7a:76:ac:7d:
+ bd:d9:24:d5:b8:b7:fc:cd:f0:42:e0:eb:78:88:56:
+ 5e:9b:9a:54:1d:4d:0c:8a:f6:d3:cf:70:f4:52:b5:
+ d8:93:04:e3:46:86:71:41:4a:2b:f0:2a:2c:55:03:
+ d6:48:c3:e0:39:38:ed:f2:5c:3c:3f:44:bc:93:3d:
+ 61:ab:4e:cd:0d:be:f0:20:27:58:0e:44:7f:04:1a:
+ 87:a5:d7:96:14:36:90:d0:49:7b:a1:75:fb:1a:6b:
+ 73:b1:f8:ce:a9:09:2c:f2:53:d5:c3:14:44:b8:86:
+ a5:f6:8b:2b:39:da:a3:33:54:d9:fa:72:1a:f7:22:
+ 15:1c:88:91:6b:7f:66:e5:c3:6a:80:b0:24:f3:df:
+ 86:45:88:fd:19:7f:75:87:1f:1f:b1:1b:0a:73:24:
+ 5b:b9:65:e0:2c:54:c8:60:d3:66:17:3f:e1:cc:54:
+ 33:73:91:02:3a:a6:7f:7b:76:39:a2:1f:96:b6:38:
+ ae:b5:c8:93:74:1d:9e:b9:b4:e5:60:9d:2f:56:d1:
+ e0:eb:5e:5b:4c:12:70:0c:6c:44:20:ab:11:d8:f4:
+ 19:f6:d2:9c:52:37:e7:fa:b6:c2:31:3b:4a:d4:14:
+ 99:ad:c7:1a:f5:5d:5f:fa:07:b8:7c:0d:1f:d6:83:
+ 1e:b3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ B5:03:F7:76:3B:61:82:6A:12:AA:18:53:EB:03:21:94:BF:FE:CE:CA
+ Signature Algorithm: sha256WithRSAEncryption
+ 56:3d:ef:94:d5:bd:da:73:b2:58:be:ae:90:ad:98:27:97:fe:
+ 01:b1:b0:52:00:b8:4d:e4:1b:21:74:1b:7e:c0:ee:5e:69:2a:
+ 25:af:5c:d6:1d:da:d2:79:c9:f3:97:29:e0:86:87:de:04:59:
+ 0f:f1:59:d4:64:85:4b:99:af:25:04:1e:c9:46:a9:97:de:82:
+ b2:1b:70:9f:9c:f6:af:71:31:dd:7b:05:a5:2c:d3:b9:ca:47:
+ f6:ca:f2:f6:e7:ad:b9:48:3f:bc:16:b7:c1:6d:f4:ea:09:af:
+ ec:f3:b5:e7:05:9e:a6:1e:8a:53:51:d6:93:81:cc:74:93:f6:
+ b9:da:a6:25:05:74:79:5a:7e:40:3e:82:4b:26:11:30:6e:e1:
+ 3f:41:c7:47:00:35:d5:f5:d3:f7:54:3e:81:3d:da:49:6a:9a:
+ b3:ef:10:3d:e6:eb:6f:d1:c8:22:47:cb:cc:cf:01:31:92:d9:
+ 18:e3:22:be:09:1e:1a:3e:5a:b2:e4:6b:0c:54:7a:7d:43:4e:
+ b8:89:a5:7b:d7:a2:3d:96:86:cc:f2:26:34:2d:6a:92:9d:9a:
+ 1a:d0:30:e2:5d:4e:04:b0:5f:8b:20:7e:77:c1:3d:95:82:d1:
+ 46:9a:3b:3c:78:b8:6f:a1:d0:0d:64:a2:78:1e:29:4e:93:c3:
+ a4:54:14:5b
+SHA1 Fingerprint=55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1
diff --git a/luni/src/main/java/java/beans/PropertyChangeSupport.java b/luni/src/main/java/java/beans/PropertyChangeSupport.java
index 04f8155..1db12b7 100644
--- a/luni/src/main/java/java/beans/PropertyChangeSupport.java
+++ b/luni/src/main/java/java/beans/PropertyChangeSupport.java
@@ -66,7 +66,7 @@
*/
public PropertyChangeSupport(Object sourceBean) {
if (sourceBean == null) {
- throw new NullPointerException();
+ throw new NullPointerException("sourceBean == null");
}
this.sourceBean = sourceBean;
}
diff --git a/luni/src/main/java/java/io/BufferedWriter.java b/luni/src/main/java/java/io/BufferedWriter.java
index e4cbe7c..55ae121 100644
--- a/luni/src/main/java/java/io/BufferedWriter.java
+++ b/luni/src/main/java/java/io/BufferedWriter.java
@@ -163,33 +163,33 @@
/**
* Writes {@code count} characters starting at {@code offset} in
- * {@code cbuf} to this writer. If {@code count} is greater than this
+ * {@code buffer} to this writer. If {@code count} is greater than this
* writer's buffer, then the buffer is flushed and the characters are
* written directly to the target writer.
*
- * @param cbuf
+ * @param buffer
* the array containing characters to write.
* @param offset
- * the start position in {@code cbuf} for retrieving characters.
+ * the start position in {@code buffer} for retrieving characters.
* @param count
* the maximum number of characters to write.
* @throws IndexOutOfBoundsException
* if {@code offset < 0} or {@code count < 0}, or if
* {@code offset + count} is greater than the size of
- * {@code cbuf}.
+ * {@code buffer}.
* @throws IOException
* if this writer is closed or another I/O error occurs.
*/
@Override
- public void write(char[] cbuf, int offset, int count) throws IOException {
+ public void write(char[] buffer, int offset, int count) throws IOException {
synchronized (lock) {
checkNotClosed();
- if (cbuf == null) {
+ if (buffer == null) {
throw new NullPointerException("buffer == null");
}
- Arrays.checkOffsetAndCount(cbuf.length, offset, count);
+ Arrays.checkOffsetAndCount(buffer.length, offset, count);
if (pos == 0 && count >= this.buf.length) {
- out.write(cbuf, offset, count);
+ out.write(buffer, offset, count);
return;
}
int available = this.buf.length - pos;
@@ -197,7 +197,7 @@
available = count;
}
if (available > 0) {
- System.arraycopy(cbuf, offset, this.buf, pos, available);
+ System.arraycopy(buffer, offset, this.buf, pos, available);
pos += available;
}
if (pos == this.buf.length) {
@@ -207,11 +207,11 @@
offset += available;
available = count - available;
if (available >= this.buf.length) {
- out.write(cbuf, offset, available);
+ out.write(buffer, offset, available);
return;
}
- System.arraycopy(cbuf, offset, this.buf, pos, available);
+ System.arraycopy(buffer, offset, this.buf, pos, available);
pos += available;
}
}
diff --git a/luni/src/main/java/java/io/ByteArrayOutputStream.java b/luni/src/main/java/java/io/ByteArrayOutputStream.java
index 3ab2c20..ff9c7df 100644
--- a/luni/src/main/java/java/io/ByteArrayOutputStream.java
+++ b/luni/src/main/java/java/io/ByteArrayOutputStream.java
@@ -162,17 +162,17 @@
/**
* Returns the contents of this ByteArrayOutputStream as a string converted
- * according to the encoding declared in {@code enc}.
+ * according to the encoding declared in {@code charsetName}.
*
- * @param enc
+ * @param charsetName
* a string representing the encoding to use when translating
* this stream to a string.
* @return this stream's current contents as an encoded string.
* @throws UnsupportedEncodingException
* if the provided encoding is not supported.
*/
- public String toString(String enc) throws UnsupportedEncodingException {
- return new String(buf, 0, count, enc);
+ public String toString(String charsetName) throws UnsupportedEncodingException {
+ return new String(buf, 0, count, charsetName);
}
/**
diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java
index 968f021..ec87fed 100644
--- a/luni/src/main/java/java/io/File.java
+++ b/luni/src/main/java/java/io/File.java
@@ -147,7 +147,7 @@
*/
public File(String dirPath, String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (dirPath == null || dirPath.isEmpty()) {
this.path = fixSlashes(name);
@@ -855,57 +855,65 @@
}
/**
- * Creates the directory named by the trailing filename of this file. Does
- * not create the complete path required to create this directory.
+ * Creates the directory named by this file, assuming its parents exist.
+ * Use {@link #mkdirs} if you also want to create missing parents.
*
* <p>Note that this method does <i>not</i> throw {@code IOException} on failure.
- * Callers must check the return value.
+ * Callers must check the return value. Note also that this method returns
+ * false if the directory already existed. If you want to know whether the
+ * directory exists on return, either use {@code (f.mkdir() || f.isDirectory())}
+ * or simply ignore the return value from this method and simply call {@link #isDirectory}.
*
- * @return {@code true} if the directory has been created, {@code false}
- * otherwise.
- * @see #mkdirs
+ * @return {@code true} if the directory was created,
+ * {@code false} on failure or if the directory already existed.
*/
public boolean mkdir() {
try {
- // On Android, we don't want default permissions to allow global access.
- Libcore.os.mkdir(path, S_IRWXU);
+ mkdirErrno();
return true;
} catch (ErrnoException errnoException) {
return false;
}
}
+ private void mkdirErrno() throws ErrnoException {
+ // On Android, we don't want default permissions to allow global access.
+ Libcore.os.mkdir(path, S_IRWXU);
+ }
+
/**
- * Creates the directory named by the trailing filename of this file,
- * including the complete directory path required to create this directory.
+ * Creates the directory named by this file, creating missing parent
+ * directories if necessary.
+ * Use {@link #mkdir} if you don't want to create missing parents.
*
* <p>Note that this method does <i>not</i> throw {@code IOException} on failure.
- * Callers must check the return value.
+ * Callers must check the return value. Note also that this method returns
+ * false if the directory already existed. If you want to know whether the
+ * directory exists on return, either use {@code (f.mkdirs() || f.isDirectory())}
+ * or simply ignore the return value from this method and simply call {@link #isDirectory}.
*
- * @return {@code true} if the necessary directories have been created,
- * {@code false} if the target directory already exists or one of
- * the directories can not be created.
- * @see #mkdir
+ * @return {@code true} if the directory was created,
+ * {@code false} on failure or if the directory already existed.
*/
public boolean mkdirs() {
- /* If the terminal directory already exists, answer false */
- if (exists()) {
- return false;
- }
+ return mkdirs(false);
+ }
- /* If the receiver can be created, answer true */
- if (mkdir()) {
+ private boolean mkdirs(boolean resultIfExists) {
+ try {
+ // Try to create the directory directly.
+ mkdirErrno();
return true;
- }
-
- String parentDir = getParent();
- /* If there is no parent and we were not created, answer false */
- if (parentDir == null) {
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno == ENOENT) {
+ // If the parent was missing, try to create it and then try again.
+ File parent = getParentFile();
+ return parent != null && parent.mkdirs(true) && mkdir();
+ } else if (errnoException.errno == EEXIST) {
+ return resultIfExists;
+ }
return false;
}
-
- /* Otherwise, try to create a parent directory and then this directory */
- return (new File(parentDir).mkdirs() && mkdir());
}
/**
diff --git a/luni/src/main/java/java/io/FileDescriptor.java b/luni/src/main/java/java/io/FileDescriptor.java
index f04ae2c..e4eb06cc 100644
--- a/luni/src/main/java/java/io/FileDescriptor.java
+++ b/luni/src/main/java/java/io/FileDescriptor.java
@@ -68,7 +68,11 @@
*/
public void sync() throws SyncFailedException {
try {
- Libcore.os.fsync(this);
+ if (Libcore.os.isatty(this)) {
+ Libcore.os.tcdrain(this);
+ } else {
+ Libcore.os.fsync(this);
+ }
} catch (ErrnoException errnoException) {
SyncFailedException sfe = new SyncFailedException(errnoException.getMessage());
sfe.initCause(errnoException);
diff --git a/luni/src/main/java/java/io/InputStreamReader.java b/luni/src/main/java/java/io/InputStreamReader.java
index 59be9ed..d3650dc 100644
--- a/luni/src/main/java/java/io/InputStreamReader.java
+++ b/luni/src/main/java/java/io/InputStreamReader.java
@@ -62,32 +62,32 @@
/**
* Constructs a new InputStreamReader on the InputStream {@code in}. The
* character converter that is used to decode bytes into characters is
- * identified by name by {@code enc}. If the encoding cannot be found, an
+ * identified by name by {@code charsetName}. If the encoding cannot be found, an
* UnsupportedEncodingException error is thrown.
*
* @param in
* the InputStream from which to read characters.
- * @param enc
+ * @param charsetName
* identifies the character converter to use.
* @throws NullPointerException
- * if {@code enc} is {@code null}.
+ * if {@code charsetName} is {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code enc} cannot be found.
+ * if the encoding specified by {@code charsetName} cannot be found.
*/
- public InputStreamReader(InputStream in, final String enc)
+ public InputStreamReader(InputStream in, final String charsetName)
throws UnsupportedEncodingException {
super(in);
- if (enc == null) {
- throw new NullPointerException();
+ if (charsetName == null) {
+ throw new NullPointerException("charsetName == null");
}
this.in = in;
try {
- decoder = Charset.forName(enc).newDecoder().onMalformedInput(
+ decoder = Charset.forName(charsetName).newDecoder().onMalformedInput(
CodingErrorAction.REPLACE).onUnmappableCharacter(
CodingErrorAction.REPLACE);
} catch (IllegalArgumentException e) {
throw (UnsupportedEncodingException)
- new UnsupportedEncodingException(enc).initCause(e);
+ new UnsupportedEncodingException(charsetName).initCause(e);
}
bytes.limit(0);
}
diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java
index 4541f1b..0476901 100644
--- a/luni/src/main/java/java/io/ObjectInputStream.java
+++ b/luni/src/main/java/java/io/ObjectInputStream.java
@@ -23,8 +23,8 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
-import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -1089,8 +1089,11 @@
for (ObjectStreamField fieldDesc : fields) {
Field field = classDesc.getReflectionField(fieldDesc);
- // We may not have been able to find the field, but we still need to read the value
- // and do the other checking, so there's no null check on 'field' here.
+ if (field != null && Modifier.isTransient(field.getModifiers())) {
+ field = null; // No setting transient fields! (http://b/4471249)
+ }
+ // We may not have been able to find the field, or it may be transient, but we still
+ // need to read the value and do the other checking...
try {
Class<?> type = fieldDesc.getTypeInternal();
if (type == byte.class) {
@@ -2341,7 +2344,7 @@
public int skipBytes(int length) throws IOException {
// To be used with available. Ok to call if reading primitive buffer
if (input == null) {
- throw new NullPointerException();
+ throw new NullPointerException("source stream is null");
}
int offset = 0;
diff --git a/luni/src/main/java/java/io/ObjectStreamClass.java b/luni/src/main/java/java/io/ObjectStreamClass.java
index e87fcd4..a28489a 100644
--- a/luni/src/main/java/java/io/ObjectStreamClass.java
+++ b/luni/src/main/java/java/io/ObjectStreamClass.java
@@ -481,16 +481,14 @@
Field field = fields[i];
int modifiers = field.getModifiers() & FIELD_MODIFIERS_MASK;
- boolean skip = Modifier.isPrivate(modifiers)
- && (Modifier.isTransient(modifiers) || Modifier
- .isStatic(modifiers));
+ boolean skip = Modifier.isPrivate(modifiers) &&
+ (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers));
if (!skip) {
// write name, modifier & "descriptor" of all but private
// static and private transient
output.writeUTF(field.getName());
output.writeInt(modifiers);
- output
- .writeUTF(descriptorForFieldSignature(getFieldSignature(field)));
+ output.writeUTF(descriptorForFieldSignature(getFieldSignature(field)));
}
}
diff --git a/luni/src/main/java/java/io/ObjectStreamField.java b/luni/src/main/java/java/io/ObjectStreamField.java
index db450e0..78a6903 100644
--- a/luni/src/main/java/java/io/ObjectStreamField.java
+++ b/luni/src/main/java/java/io/ObjectStreamField.java
@@ -58,8 +58,10 @@
* if {@code name} or {@code cl} is {@code null}.
*/
public ObjectStreamField(String name, Class<?> cl) {
- if (name == null || cl == null) {
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ } else if (cl == null) {
+ throw new NullPointerException("cl == null");
}
this.name = name;
this.type = new WeakReference<Class<?>>(cl);
@@ -81,8 +83,10 @@
* @see ObjectOutputStream#writeUnshared(Object)
*/
public ObjectStreamField(String name, Class<?> cl, boolean unshared) {
- if (name == null || cl == null) {
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ } else if (cl == null) {
+ throw new NullPointerException("cl == null");
}
this.name = name;
this.type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl);
@@ -100,7 +104,7 @@
*/
ObjectStreamField(String signature, String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
this.name = name;
this.typeString = signature.replace('.', '/').intern();
diff --git a/luni/src/main/java/java/io/OutputStreamWriter.java b/luni/src/main/java/java/io/OutputStreamWriter.java
index 86b62fc..5dffdfe 100644
--- a/luni/src/main/java/java/io/OutputStreamWriter.java
+++ b/luni/src/main/java/java/io/OutputStreamWriter.java
@@ -57,30 +57,30 @@
/**
* Constructs a new OutputStreamWriter using {@code out} as the target
- * stream to write converted characters to and {@code enc} as the character
+ * stream to write converted characters to and {@code charsetName} as the character
* encoding. If the encoding cannot be found, an
* UnsupportedEncodingException error is thrown.
*
* @param out
* the target stream to write converted bytes to.
- * @param enc
+ * @param charsetName
* the string describing the desired character encoding.
* @throws NullPointerException
- * if {@code enc} is {@code null}.
+ * if {@code charsetName} is {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code enc} cannot be found.
+ * if the encoding specified by {@code charsetName} cannot be found.
*/
- public OutputStreamWriter(OutputStream out, final String enc)
+ public OutputStreamWriter(OutputStream out, final String charsetName)
throws UnsupportedEncodingException {
super(out);
- if (enc == null) {
- throw new NullPointerException();
+ if (charsetName == null) {
+ throw new NullPointerException("charsetName == null");
}
this.out = out;
try {
- encoder = Charset.forName(enc).newEncoder();
+ encoder = Charset.forName(charsetName).newEncoder();
} catch (Exception e) {
- throw new UnsupportedEncodingException(enc);
+ throw new UnsupportedEncodingException(charsetName);
}
encoder.onMalformedInput(CodingErrorAction.REPLACE);
encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
@@ -106,19 +106,19 @@
/**
* Constructs a new OutputStreamWriter using {@code out} as the target
- * stream to write converted characters to and {@code enc} as the character
+ * stream to write converted characters to and {@code charsetEncoder} as the character
* encoder.
*
* @param out
* the target stream to write converted bytes to.
- * @param enc
+ * @param charsetEncoder
* the character encoder used for character conversion.
*/
- public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {
+ public OutputStreamWriter(OutputStream out, CharsetEncoder charsetEncoder) {
super(out);
- enc.charset();
+ charsetEncoder.charset();
this.out = out;
- encoder = enc;
+ encoder = charsetEncoder;
}
/**
diff --git a/luni/src/main/java/java/io/PipedOutputStream.java b/luni/src/main/java/java/io/PipedOutputStream.java
index a674bb3..1b139e9 100644
--- a/luni/src/main/java/java/io/PipedOutputStream.java
+++ b/luni/src/main/java/java/io/PipedOutputStream.java
@@ -81,7 +81,7 @@
*/
public void connect(PipedInputStream stream) throws IOException {
if (stream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("stream == null");
}
synchronized (stream) {
if (this.target != null) {
diff --git a/luni/src/main/java/java/io/PipedWriter.java b/luni/src/main/java/java/io/PipedWriter.java
index ece899a..ad8974b 100644
--- a/luni/src/main/java/java/io/PipedWriter.java
+++ b/luni/src/main/java/java/io/PipedWriter.java
@@ -85,7 +85,7 @@
*/
public void connect(PipedReader reader) throws IOException {
if (reader == null) {
- throw new NullPointerException();
+ throw new NullPointerException("reader == null");
}
synchronized (reader) {
if (this.destination != null) {
diff --git a/luni/src/main/java/java/io/PrintStream.java b/luni/src/main/java/java/io/PrintStream.java
index ba48b04..18f2310 100644
--- a/luni/src/main/java/java/io/PrintStream.java
+++ b/luni/src/main/java/java/io/PrintStream.java
@@ -59,7 +59,7 @@
public PrintStream(OutputStream out) {
super(out);
if (out == null) {
- throw new NullPointerException();
+ throw new NullPointerException("out == null");
}
}
@@ -80,14 +80,14 @@
public PrintStream(OutputStream out, boolean autoFlush) {
super(out);
if (out == null) {
- throw new NullPointerException();
+ throw new NullPointerException("out == null");
}
this.autoFlush = autoFlush;
}
/**
* Constructs a new {@code PrintStream} with {@code out} as its target
- * stream and using the character encoding {@code enc} while writing. The
+ * stream and using the character encoding {@code charsetName} while writing. The
* parameter {@code autoFlush} determines if the print stream automatically
* flushes its contents to the target stream when a newline is encountered.
*
@@ -96,28 +96,30 @@
* @param autoFlush
* indicates whether or not to flush contents upon encountering a
* newline sequence.
- * @param enc
+ * @param charsetName
* the non-null string describing the desired character encoding.
* @throws NullPointerException
- * if {@code out} or {@code enc} are {@code null}.
+ * if {@code out} or {@code charsetName} are {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code enc} is not supported.
+ * if the encoding specified by {@code charsetName} is not supported.
*/
- public PrintStream(OutputStream out, boolean autoFlush, String enc)
+ public PrintStream(OutputStream out, boolean autoFlush, String charsetName)
throws UnsupportedEncodingException {
super(out);
- if (out == null || enc == null) {
- throw new NullPointerException();
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ } else if (charsetName == null) {
+ throw new NullPointerException("charsetName == null");
}
this.autoFlush = autoFlush;
try {
- if (!Charset.isSupported(enc)) {
- throw new UnsupportedEncodingException(enc);
+ if (!Charset.isSupported(charsetName)) {
+ throw new UnsupportedEncodingException(charsetName);
}
} catch (IllegalCharsetNameException e) {
- throw new UnsupportedEncodingException(enc);
+ throw new UnsupportedEncodingException(charsetName);
}
- encoding = enc;
+ encoding = charsetName;
}
/**
@@ -136,30 +138,30 @@
/**
* Constructs a new {@code PrintStream} with {@code file} as its target. The
- * character set named {@code csn} is used for character encoding.
+ * character set named {@code charsetName} is used for character encoding.
*
* @param file
* the target file. If the file already exists, its contents are
* removed, otherwise a new file is created.
- * @param csn
+ * @param charsetName
* the name of the character set used for character encoding.
* @throws FileNotFoundException
* if an error occurs while opening or creating the target file.
* @throws NullPointerException
- * if {@code csn} is {@code null}.
+ * if {@code charsetName} is {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code csn} is not supported.
+ * if the encoding specified by {@code charsetName} is not supported.
*/
- public PrintStream(File file, String csn) throws FileNotFoundException,
+ public PrintStream(File file, String charsetName) throws FileNotFoundException,
UnsupportedEncodingException {
super(new FileOutputStream(file));
- if (csn == null) {
- throw new NullPointerException();
+ if (charsetName == null) {
+ throw new NullPointerException("charsetName == null");
}
- if (!Charset.isSupported(csn)) {
- throw new UnsupportedEncodingException(csn);
+ if (!Charset.isSupported(charsetName)) {
+ throw new UnsupportedEncodingException(charsetName);
}
- encoding = csn;
+ encoding = charsetName;
}
/**
@@ -179,24 +181,24 @@
/**
* Constructs a new {@code PrintStream} with the file identified by
- * {@code fileName} as its target. The character set named {@code csn} is
+ * {@code fileName} as its target. The character set named {@code charsetName} is
* used for character encoding.
*
* @param fileName
* the target file's name. If the file already exists, its
* contents are removed, otherwise a new file is created.
- * @param csn
+ * @param charsetName
* the name of the character set used for character encoding.
* @throws FileNotFoundException
* if an error occurs while opening or creating the target file.
* @throws NullPointerException
- * if {@code csn} is {@code null}.
+ * if {@code charsetName} is {@code null}.
* @throws UnsupportedEncodingException
- * if the encoding specified by {@code csn} is not supported.
+ * if the encoding specified by {@code charsetName} is not supported.
*/
- public PrintStream(String fileName, String csn)
+ public PrintStream(String fileName, String charsetName)
throws FileNotFoundException, UnsupportedEncodingException {
- this(new File(fileName), csn);
+ this(new File(fileName), charsetName);
}
/**
diff --git a/luni/src/main/java/java/io/Reader.java b/luni/src/main/java/java/io/Reader.java
index 310a57c..e947d08 100644
--- a/luni/src/main/java/java/io/Reader.java
+++ b/luni/src/main/java/java/io/Reader.java
@@ -61,7 +61,7 @@
*/
protected Reader(Object lock) {
if (lock == null) {
- throw new NullPointerException();
+ throw new NullPointerException("lock == null");
}
this.lock = lock;
}
diff --git a/luni/src/main/java/java/io/SequenceInputStream.java b/luni/src/main/java/java/io/SequenceInputStream.java
index 9ae1901..8333834 100644
--- a/luni/src/main/java/java/io/SequenceInputStream.java
+++ b/luni/src/main/java/java/io/SequenceInputStream.java
@@ -50,7 +50,7 @@
*/
public SequenceInputStream(InputStream s1, InputStream s2) {
if (s1 == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s1 == null");
}
Vector<InputStream> inVector = new Vector<InputStream>(1);
inVector.addElement(s2);
@@ -73,7 +73,7 @@
if (e.hasMoreElements()) {
in = e.nextElement();
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("element is null");
}
}
}
@@ -112,7 +112,7 @@
if (e.hasMoreElements()) {
in = e.nextElement();
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("element is null");
}
} else {
in = null;
diff --git a/luni/src/main/java/java/io/StreamTokenizer.java b/luni/src/main/java/java/io/StreamTokenizer.java
index 0522be6..a16dc4b 100644
--- a/luni/src/main/java/java/io/StreamTokenizer.java
+++ b/luni/src/main/java/java/io/StreamTokenizer.java
@@ -167,7 +167,7 @@
public StreamTokenizer(InputStream is) {
this();
if (is == null) {
- throw new NullPointerException();
+ throw new NullPointerException("is == null");
}
inStream = is;
}
@@ -194,7 +194,7 @@
public StreamTokenizer(Reader r) {
this();
if (r == null) {
- throw new NullPointerException();
+ throw new NullPointerException("r == null");
}
inReader = r;
}
diff --git a/luni/src/main/java/java/io/StringBufferInputStream.java b/luni/src/main/java/java/io/StringBufferInputStream.java
index 1fada57..1768abe 100644
--- a/luni/src/main/java/java/io/StringBufferInputStream.java
+++ b/luni/src/main/java/java/io/StringBufferInputStream.java
@@ -54,7 +54,7 @@
*/
public StringBufferInputStream(String str) {
if (str == null) {
- throw new NullPointerException();
+ throw new NullPointerException("str == null");
}
buffer = str;
count = str.length();
diff --git a/luni/src/main/java/java/io/Writer.java b/luni/src/main/java/java/io/Writer.java
index 2e28b80..33d7604 100644
--- a/luni/src/main/java/java/io/Writer.java
+++ b/luni/src/main/java/java/io/Writer.java
@@ -59,7 +59,7 @@
*/
protected Writer(Object lock) {
if (lock == null) {
- throw new NullPointerException();
+ throw new NullPointerException("lock == null");
}
this.lock = lock;
}
diff --git a/luni/src/main/java/java/lang/AbstractStringBuilder.java b/luni/src/main/java/java/lang/AbstractStringBuilder.java
index baab47d..c3107f2 100644
--- a/luni/src/main/java/java/lang/AbstractStringBuilder.java
+++ b/luni/src/main/java/java/lang/AbstractStringBuilder.java
@@ -77,7 +77,7 @@
AbstractStringBuilder(int capacity) {
if (capacity < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(capacity));
}
value = new char[capacity];
}
@@ -437,7 +437,7 @@
}
if (start == end) {
if (string == null) {
- throw new NullPointerException();
+ throw new NullPointerException("string == null");
}
insert0(start, string);
return;
diff --git a/luni/src/main/java/java/lang/Character.java b/luni/src/main/java/java/lang/Character.java
index 1a41ec2..cf0ab84 100644
--- a/luni/src/main/java/java/lang/Character.java
+++ b/luni/src/main/java/java/lang/Character.java
@@ -532,7 +532,7 @@
*/
protected Subset(String string) {
if (string == null) {
- throw new NullPointerException();
+ throw new NullPointerException("string == null");
}
name = string;
}
@@ -1502,7 +1502,7 @@
*/
public static UnicodeBlock forName(String blockName) {
if (blockName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("blockName == null");
}
int block = forNameImpl(blockName);
if (block == -1) {
@@ -1798,7 +1798,7 @@
*/
public static int codePointAt(CharSequence seq, int index) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length();
if (index < 0 || index >= len) {
@@ -1840,7 +1840,7 @@
*/
public static int codePointAt(char[] seq, int index) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length;
if (index < 0 || index >= len) {
@@ -1923,7 +1923,7 @@
*/
public static int codePointBefore(CharSequence seq, int index) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length();
if (index < 1 || index > len) {
@@ -1965,7 +1965,7 @@
*/
public static int codePointBefore(char[] seq, int index) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length;
if (index < 1 || index > len) {
@@ -2012,7 +2012,7 @@
*/
public static int codePointBefore(char[] seq, int index, int start) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length;
if (index <= start || index > len || start < 0 || start >= len) {
@@ -2055,7 +2055,7 @@
public static int toChars(int codePoint, char[] dst, int dstIndex) {
checkValidCodePoint(codePoint);
if (dst == null) {
- throw new NullPointerException();
+ throw new NullPointerException("dst == null");
}
if (dstIndex < 0 || dstIndex >= dst.length) {
throw new IndexOutOfBoundsException();
@@ -2126,7 +2126,7 @@
public static int codePointCount(CharSequence seq, int beginIndex,
int endIndex) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length();
if (beginIndex < 0 || endIndex > len || beginIndex > endIndex) {
@@ -2215,7 +2215,7 @@
*/
public static int offsetByCodePoints(CharSequence seq, int index, int codePointOffset) {
if (seq == null) {
- throw new NullPointerException();
+ throw new NullPointerException("seq == null");
}
int len = seq.length();
if (index < 0 || index > len) {
diff --git a/luni/src/main/java/java/lang/ClassLoader.java b/luni/src/main/java/java/lang/ClassLoader.java
index 0cdc448..c99d57c 100644
--- a/luni/src/main/java/java/lang/ClassLoader.java
+++ b/luni/src/main/java/java/lang/ClassLoader.java
@@ -195,7 +195,7 @@
*/
ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
if (parentLoader == null && !nullAllowed) {
- throw new NullPointerException("Parent ClassLoader may not be null");
+ throw new NullPointerException("parentLoader == null && !nullAllowed");
}
parent = parentLoader;
}
diff --git a/luni/src/main/java/java/lang/Daemons.java b/luni/src/main/java/java/lang/Daemons.java
index a7d0964..78a4152 100644
--- a/luni/src/main/java/java/lang/Daemons.java
+++ b/luni/src/main/java/java/lang/Daemons.java
@@ -31,8 +31,9 @@
* @hide
*/
public final class Daemons {
- private static final int NANOS_PER_MILLI = 1000000;
- private static final long MAX_FINALIZE_MILLIS = 10L * 1000L; // 10 seconds
+ private static final int NANOS_PER_MILLI = 1000 * 1000;
+ private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
+ private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
public static void start() {
ReferenceQueueDaemon.INSTANCE.start();
@@ -203,41 +204,78 @@
@Override public void run() {
while (isRunning()) {
- try {
- Object object = FinalizerDaemon.INSTANCE.finalizingObject;
- long startedNanos = FinalizerDaemon.INSTANCE.finalizingStartedNanos;
-
- if (object == null) {
- synchronized (this) {
- // wait until something is being finalized
- // http://code.google.com/p/android/issues/detail?id=22778
- wait();
- continue;
- }
- }
-
- long elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI;
- long sleepMillis = MAX_FINALIZE_MILLIS - elapsedMillis;
- if (sleepMillis > 0) {
- Thread.sleep(sleepMillis);
- elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI;
- }
-
- if (object != FinalizerDaemon.INSTANCE.finalizingObject
- || VMRuntime.getRuntime().isDebuggerActive()) {
- continue;
- }
-
- // The current object has exceeded the finalization deadline; abort!
- Exception syntheticException = new TimeoutException();
- syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
- System.logE(object.getClass().getName() + ".finalize() timed out after "
- + elapsedMillis + " ms; limit is " + MAX_FINALIZE_MILLIS + " ms",
- syntheticException);
- System.exit(2);
- } catch (InterruptedException ignored) {
+ Object object = waitForObject();
+ if (object == null) {
+ // We have been interrupted, need to see if this daemon has been stopped.
+ continue;
+ }
+ boolean finalized = waitForFinalization(object);
+ if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) {
+ finalizerTimedOut(object);
+ break;
}
}
}
+
+ private Object waitForObject() {
+ while (true) {
+ Object object = FinalizerDaemon.INSTANCE.finalizingObject;
+ if (object != null) {
+ return object;
+ }
+ synchronized (this) {
+ // wait until something is ready to be finalized
+ // http://code.google.com/p/android/issues/detail?id=22778
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // Daemon.stop may have interrupted us.
+ return null;
+ }
+ }
+ }
+ }
+
+ private void sleepFor(long startNanos, long durationNanos) {
+ while (true) {
+ long elapsedNanos = System.nanoTime() - startNanos;
+ long sleepNanos = durationNanos - elapsedNanos;
+ long sleepMills = sleepNanos / NANOS_PER_MILLI;
+ if (sleepMills <= 0) {
+ return;
+ }
+ try {
+ Thread.sleep(sleepMills);
+ } catch (InterruptedException e) {
+ if (!isRunning()) {
+ return;
+ }
+ }
+ }
+ }
+
+ private boolean waitForFinalization(Object object) {
+ sleepFor(FinalizerDaemon.INSTANCE.finalizingStartedNanos, MAX_FINALIZE_NANOS);
+ return object != FinalizerDaemon.INSTANCE.finalizingObject;
+ }
+
+ private static void finalizerTimedOut(Object object) {
+ // The current object has exceeded the finalization deadline; abort!
+ String message = object.getClass().getName() + ".finalize() timed out after "
+ + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds";
+ Exception syntheticException = new TimeoutException(message);
+ // We use the stack from where finalize() was running to show where it was stuck.
+ syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
+ Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
+ if (h == null) {
+ // If we have no handler, log and exit.
+ System.logE(message, syntheticException);
+ System.exit(2);
+ }
+ // Otherwise call the handler to do crash reporting.
+ // We don't just throw because we're not the thread that
+ // timed out; we're the thread that detected it.
+ h.uncaughtException(Thread.currentThread(), syntheticException);
+ }
}
}
diff --git a/luni/src/main/java/java/lang/Enum.java b/luni/src/main/java/java/lang/Enum.java
index 391670c..7a0f514 100644
--- a/luni/src/main/java/java/lang/Enum.java
+++ b/luni/src/main/java/java/lang/Enum.java
@@ -34,6 +34,9 @@
private static final BasicLruCache<Class<? extends Enum>, Object[]> sharedConstantsCache
= new BasicLruCache<Class<? extends Enum>, Object[]>(64) {
@Override protected Object[] create(Class<? extends Enum> enumType) {
+ if (!enumType.isEnum()) {
+ return null;
+ }
Method method = (Method) Class.getDeclaredConstructorOrMethod(
enumType, "values", EmptyArray.CLASS);
try {
@@ -178,13 +181,16 @@
* have a constant value called {@code name}.
*/
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
- if (enumType == null || name == null) {
- throw new NullPointerException("enumType == null || name == null");
+ if (enumType == null) {
+ throw new NullPointerException("enumType == null");
+ } else if (name == null) {
+ throw new NullPointerException("name == null");
}
- if (!enumType.isEnum()) {
+ T[] values = getSharedConstants(enumType);
+ if (values == null) {
throw new IllegalArgumentException(enumType + " is not an enum type");
}
- for (T value : getSharedConstants(enumType)) {
+ for (T value : values) {
if (name.equals(value.name())) {
return value;
}
diff --git a/luni/src/main/java/java/lang/Math.java b/luni/src/main/java/java/lang/Math.java
index 68db4fa..22c89ce 100644
--- a/luni/src/main/java/java/lang/Math.java
+++ b/luni/src/main/java/java/lang/Math.java
@@ -53,10 +53,6 @@
* <li>{@code abs(-infinity) = +infinity}</li>
* <li>{@code abs(NaN) = NaN}</li>
* </ul>
- *
- * @param d
- * the value whose absolute value has to be computed.
- * @return the absolute value of the argument.
*/
public static native double abs(double d);
@@ -70,11 +66,6 @@
* <li>{@code abs(-infinity) = +infinity}</li>
* <li>{@code abs(NaN) = NaN}</li>
* </ul>
- *
- * @param f
- * the value whose absolute value has to be computed.
- * @return the argument if it is positive, otherwise the negation of the
- * argument.
*/
public static native float abs(float f);
@@ -83,22 +74,12 @@
* <p>
* If the argument is {@code Integer.MIN_VALUE}, {@code Integer.MIN_VALUE}
* is returned.
- *
- * @param i
- * the value whose absolute value has to be computed.
- * @return the argument if it is positive, otherwise the negation of the
- * argument.
*/
public static native int abs(int i);
/**
* Returns the absolute value of the argument. If the argument is {@code
* Long.MIN_VALUE}, {@code Long.MIN_VALUE} is returned.
- *
- * @param l
- * the value whose absolute value has to be computed.
- * @return the argument if it is positive, otherwise the negation of the
- * argument.
*/
public static native long abs(long l);
@@ -467,12 +448,6 @@
* <li>{@code max(+0.0, -0.0) = +0.0}</li>
* <li>{@code max(-0.0, +0.0) = +0.0}</li>
* </ul>
- *
- * @param d1
- * the first argument.
- * @param d2
- * the second argument.
- * @return the larger of {@code d1} and {@code d2}.
*/
public static double max(double d1, double d2) {
if (d1 > d2) {
@@ -504,12 +479,6 @@
* <li>{@code max(+0.0, -0.0) = +0.0}</li>
* <li>{@code max(-0.0, +0.0) = +0.0}</li>
* </ul>
- *
- * @param f1
- * the first argument.
- * @param f2
- * the second argument.
- * @return the larger of {@code f1} and {@code f2}.
*/
public static float max(float f1, float f2) {
if (f1 > f2) {
@@ -533,24 +502,12 @@
/**
* Returns the most positive (closest to positive infinity) of the two
* arguments.
- *
- * @param i1
- * the first argument.
- * @param i2
- * the second argument.
- * @return the larger of {@code i1} and {@code i2}.
*/
public static native int max(int i1, int i2);
/**
* Returns the most positive (closest to positive infinity) of the two
* arguments.
- *
- * @param l1
- * the first argument.
- * @param l2
- * the second argument.
- * @return the larger of {@code l1} and {@code l2}.
*/
public static long max(long l1, long l2) {
return l1 > l2 ? l1 : l2;
@@ -567,12 +524,6 @@
* <li>{@code min(+0.0, -0.0) = -0.0}</li>
* <li>{@code min(-0.0, +0.0) = -0.0}</li>
* </ul>
- *
- * @param d1
- * the first argument.
- * @param d2
- * the second argument.
- * @return the smaller of {@code d1} and {@code d2}.
*/
public static double min(double d1, double d2) {
if (d1 > d2) {
@@ -604,12 +555,6 @@
* <li>{@code min(+0.0, -0.0) = -0.0}</li>
* <li>{@code min(-0.0, +0.0) = -0.0}</li>
* </ul>
- *
- * @param f1
- * the first argument.
- * @param f2
- * the second argument.
- * @return the smaller of {@code f1} and {@code f2}.
*/
public static float min(float f1, float f2) {
if (f1 > f2) {
@@ -633,24 +578,12 @@
/**
* Returns the most negative (closest to negative infinity) of the two
* arguments.
- *
- * @param i1
- * the first argument.
- * @param i2
- * the second argument.
- * @return the smaller of {@code i1} and {@code i2}.
*/
public static native int min(int i1, int i2);
/**
* Returns the most negative (closest to negative infinity) of the two
* arguments.
- *
- * @param l1
- * the first argument.
- * @param l2
- * the second argument.
- * @return the smaller of {@code l1} and {@code l2}.
*/
public static long min(long l1, long l2) {
return l1 < l2 ? l1 : l2;
@@ -884,10 +817,6 @@
* <li>{@code sqrt(+infinity) = +infinity}</li>
* <li>{@code sqrt(NaN) = NaN}</li>
* </ul>
- *
- * @param d
- * the value whose square root has to be computed.
- * @return the square root of the argument.
*/
public static native double sqrt(double d);
diff --git a/luni/src/main/java/java/lang/ProcessBuilder.java b/luni/src/main/java/java/lang/ProcessBuilder.java
index 5b7efdc..57e21b6 100644
--- a/luni/src/main/java/java/lang/ProcessBuilder.java
+++ b/luni/src/main/java/java/lang/ProcessBuilder.java
@@ -59,7 +59,7 @@
*/
public ProcessBuilder(List<String> command) {
if (command == null) {
- throw new NullPointerException();
+ throw new NullPointerException("command == null");
}
this.command = command;
@@ -102,7 +102,7 @@
*/
public ProcessBuilder command(List<String> command) {
if (command == null) {
- throw new NullPointerException();
+ throw new NullPointerException("command == null");
}
this.command = command;
return this;
diff --git a/luni/src/main/java/java/lang/ProcessManager.java b/luni/src/main/java/java/lang/ProcessManager.java
index 1e820a9..28314b7 100644
--- a/luni/src/main/java/java/lang/ProcessManager.java
+++ b/luni/src/main/java/java/lang/ProcessManager.java
@@ -168,10 +168,10 @@
boolean redirectErrorStream) throws IOException {
// Make sure we throw the same exceptions as the RI.
if (taintedCommand == null) {
- throw new NullPointerException();
+ throw new NullPointerException("taintedCommand == null");
}
if (taintedCommand.length == 0) {
- throw new IndexOutOfBoundsException();
+ throw new IndexOutOfBoundsException("taintedCommand.length == 0");
}
// Handle security and safety by copying mutable inputs and checking them.
@@ -179,16 +179,16 @@
String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null;
// Check we're not passing null Strings to the native exec.
- for (String arg : command) {
- if (arg == null) {
- throw new NullPointerException();
+ for (int i = 0; i < command.length; i++) {
+ if (command[i] == null) {
+ throw new NullPointerException("taintedCommand[" + i + "] == null");
}
}
// The environment is allowed to be null or empty, but no element may be null.
if (environment != null) {
- for (String env : environment) {
- if (env == null) {
- throw new NullPointerException();
+ for (int i = 0; i < environment.length; i++) {
+ if (environment[i] == null) {
+ throw new NullPointerException("taintedEnvironment[" + i + "] == null");
}
}
}
diff --git a/luni/src/main/java/java/lang/Runtime.java b/luni/src/main/java/java/lang/Runtime.java
index 320f157..a2debfd 100644
--- a/luni/src/main/java/java/lang/Runtime.java
+++ b/luni/src/main/java/java/lang/Runtime.java
@@ -224,9 +224,9 @@
public Process exec(String prog, String[] envp, File directory) throws java.io.IOException {
// Sanity checks
if (prog == null) {
- throw new NullPointerException();
- } else if (prog.length() == 0) {
- throw new IllegalArgumentException();
+ throw new NullPointerException("prog == null");
+ } else if (prog.isEmpty()) {
+ throw new IllegalArgumentException("prog is empty");
}
// Break down into tokens, as described in Java docs
@@ -331,11 +331,11 @@
/*
* Loads and links a library without security checks.
*/
- void load(String filename, ClassLoader loader) {
- if (filename == null) {
- throw new NullPointerException("library path was null.");
+ void load(String pathName, ClassLoader loader) {
+ if (pathName == null) {
+ throw new NullPointerException("pathName == null");
}
- String error = nativeLoad(filename, loader);
+ String error = nativeLoad(pathName, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
@@ -362,8 +362,9 @@
if (loader != null) {
String filename = loader.findLibrary(libraryName);
if (filename == null) {
- throw new UnsatisfiedLinkError("Couldn't load " + libraryName + ": " +
- "findLibrary returned null");
+ throw new UnsatisfiedLinkError("Couldn't load " + libraryName
+ + " from loader " + loader
+ + ": findLibrary returned null");
}
String error = nativeLoad(filename, loader);
if (error != null) {
@@ -537,7 +538,7 @@
public void addShutdownHook(Thread hook) {
// Sanity checks
if (hook == null) {
- throw new NullPointerException("Hook may not be null.");
+ throw new NullPointerException("hook == null");
}
if (shuttingDown) {
@@ -570,7 +571,7 @@
public boolean removeShutdownHook(Thread hook) {
// Sanity checks
if (hook == null) {
- throw new NullPointerException("Hook may not be null.");
+ throw new NullPointerException("hook == null");
}
if (shuttingDown) {
diff --git a/luni/src/main/java/java/lang/StackTraceElement.java b/luni/src/main/java/java/lang/StackTraceElement.java
index b83120c..a59935a 100644
--- a/luni/src/main/java/java/lang/StackTraceElement.java
+++ b/luni/src/main/java/java/lang/StackTraceElement.java
@@ -58,8 +58,10 @@
* if {@code cls} or {@code method} is {@code null}.
*/
public StackTraceElement(String cls, String method, String file, int line) {
- if (cls == null || method == null) {
- throw new NullPointerException();
+ if (cls == null) {
+ throw new NullPointerException("cls == null");
+ } else if (method == null) {
+ throw new NullPointerException("method == null");
}
declaringClass = cls;
methodName = method;
diff --git a/luni/src/main/java/java/lang/StrictMath.java b/luni/src/main/java/java/lang/StrictMath.java
index d21027b..6571b2d 100644
--- a/luni/src/main/java/java/lang/StrictMath.java
+++ b/luni/src/main/java/java/lang/StrictMath.java
@@ -62,10 +62,6 @@
* <li>{@code abs(-infinity) = +infinity}</li>
* <li>{@code abs(NaN) = NaN}</li>
* </ul>
- *
- * @param d
- * the value whose absolute value has to be computed.
- * @return the absolute value of the argument.
*/
public static double abs(double d) {
return Math.abs(d);
@@ -81,11 +77,6 @@
* <li>{@code abs(-infinity) = +infinity}</li>
* <li>{@code abs(NaN) = NaN}</li>
* </ul>
- *
- * @param f
- * the value whose absolute value has to be computed.
- * @return the argument if it is positive, otherwise the negation of the
- * argument.
*/
public static float abs(float f) {
return Math.abs(f);
@@ -96,11 +87,6 @@
* <p>
* If the argument is {@code Integer.MIN_VALUE}, {@code Integer.MIN_VALUE}
* is returned.
- *
- * @param i
- * the value whose absolute value has to be computed.
- * @return the argument if it is positive, otherwise the negation of the
- * argument.
*/
public static int abs(int i) {
return Math.abs(i);
@@ -111,11 +97,6 @@
* <p>
* If the argument is {@code Long.MIN_VALUE}, {@code Long.MIN_VALUE} is
* returned.
- *
- * @param l
- * the value whose absolute value has to be computed.
- * @return the argument if it is positive, otherwise the negation of the
- * argument.
*/
public static long abs(long l) {
return Math.abs(l);
@@ -469,12 +450,6 @@
* <li>{@code max(+0.0, -0.0) = +0.0}</li>
* <li>{@code max(-0.0, +0.0) = +0.0}</li>
* </ul>
- *
- * @param d1
- * the first argument.
- * @param d2
- * the second argument.
- * @return the larger of {@code d1} and {@code d2}.
*/
public static double max(double d1, double d2) {
if (d1 > d2)
@@ -502,12 +477,6 @@
* <li>{@code max(+0.0, -0.0) = +0.0}</li>
* <li>{@code max(-0.0, +0.0) = +0.0}</li>
* </ul>
- *
- * @param f1
- * the first argument.
- * @param f2
- * the second argument.
- * @return the larger of {@code f1} and {@code f2}.
*/
public static float max(float f1, float f2) {
if (f1 > f2)
@@ -527,12 +496,6 @@
/**
* Returns the most positive (closest to positive infinity) of the two
* arguments.
- *
- * @param i1
- * the first argument.
- * @param i2
- * the second argument.
- * @return the larger of {@code i1} and {@code i2}.
*/
public static int max(int i1, int i2) {
return Math.max(i1, i2);
@@ -541,15 +504,9 @@
/**
* Returns the most positive (closest to positive infinity) of the two
* arguments.
- *
- * @param l1
- * the first argument.
- * @param l2
- * the second argument.
- * @return the larger of {@code l1} and {@code l2}.
*/
public static long max(long l1, long l2) {
- return Math.max(l1, l2);
+ return l1 > l2 ? l1 : l2;
}
/**
@@ -563,12 +520,6 @@
* <li>{@code min(+0.0, -0.0) = -0.0}</li>
* <li>{@code min(-0.0, +0.0) = -0.0}</li>
* </ul>
- *
- * @param d1
- * the first argument.
- * @param d2
- * the second argument.
- * @return the smaller of {@code d1} and {@code d2}.
*/
public static double min(double d1, double d2) {
if (d1 > d2)
@@ -596,12 +547,6 @@
* <li>{@code min(+0.0, -0.0) = -0.0}</li>
* <li>{@code min(-0.0, +0.0) = -0.0}</li>
* </ul>
- *
- * @param f1
- * the first argument.
- * @param f2
- * the second argument.
- * @return the smaller of {@code f1} and {@code f2}.
*/
public static float min(float f1, float f2) {
if (f1 > f2)
@@ -621,12 +566,6 @@
/**
* Returns the most negative (closest to negative infinity) of the two
* arguments.
- *
- * @param i1
- * the first argument.
- * @param i2
- * the second argument.
- * @return the smaller of {@code i1} and {@code i2}.
*/
public static int min(int i1, int i2) {
return Math.min(i1, i2);
@@ -635,15 +574,9 @@
/**
* Returns the most negative (closest to negative infinity) of the two
* arguments.
- *
- * @param l1
- * the first argument.
- * @param l2
- * the second argument.
- * @return the smaller of {@code l1} and {@code l2}.
*/
public static long min(long l1, long l2) {
- return Math.min(l1, l2);
+ return l1 < l2 ? l1 : l2;
}
/**
@@ -856,10 +789,6 @@
* <li>{@code sqrt(+infinity) = +infinity}</li>
* <li>{@code sqrt(NaN) = NaN}</li>
* </ul>
- *
- * @param d
- * the value whose square root has to be computed.
- * @return the square root of the argument.
*/
public static native double sqrt(double d);
diff --git a/luni/src/main/java/java/lang/String.java b/luni/src/main/java/java/lang/String.java
index efd4210..f3aeb64 100644
--- a/luni/src/main/java/java/lang/String.java
+++ b/luni/src/main/java/java/lang/String.java
@@ -168,17 +168,7 @@
* if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}.
*/
public String(byte[] data, int offset, int byteCount) {
- if ((offset | byteCount) < 0 || byteCount > data.length - offset) {
- throw failedBoundsCheck(data.length, offset, byteCount);
- }
- CharBuffer cb = Charset.defaultCharset().decode(ByteBuffer.wrap(data, offset, byteCount));
- this.count = cb.length();
- this.offset = 0;
- if (count > 0) {
- value = cb.array();
- } else {
- value = EmptyArray.CHAR;
- }
+ this(data, offset, byteCount, Charset.defaultCharset());
}
/**
@@ -524,7 +514,7 @@
*/
public String(int[] codePoints, int offset, int count) {
if (codePoints == null) {
- throw new NullPointerException();
+ throw new NullPointerException("codePoints == null");
}
if ((offset | count) < 0 || count > codePoints.length - offset) {
throw failedBoundsCheck(codePoints.length, offset, count);
@@ -1232,7 +1222,7 @@
*/
public boolean regionMatches(int thisStart, String string, int start, int length) {
if (string == null) {
- throw new NullPointerException();
+ throw new NullPointerException("string == null");
}
if (start < 0 || string.count - start < length) {
return false;
@@ -1729,7 +1719,7 @@
*/
public boolean contentEquals(CharSequence cs) {
if (cs == null) {
- throw new NullPointerException();
+ throw new NullPointerException("cs == null");
}
int len = cs.length();
@@ -1922,7 +1912,7 @@
*/
public boolean contains(CharSequence cs) {
if (cs == null) {
- throw new NullPointerException();
+ throw new NullPointerException("cs == null");
}
return indexOf(cs.toString()) >= 0;
}
@@ -1991,7 +1981,7 @@
*/
public static String format(Locale locale, String format, Object... args) {
if (format == null) {
- throw new NullPointerException("null format argument");
+ throw new NullPointerException("format == null");
}
int bufferSize = format.length() + (args == null ? 0 : args.length * 10);
Formatter f = new Formatter(new StringBuilder(bufferSize), locale);
diff --git a/luni/src/main/java/java/lang/StringBuilder.java b/luni/src/main/java/java/lang/StringBuilder.java
index d886100..a944e68 100644
--- a/luni/src/main/java/java/lang/StringBuilder.java
+++ b/luni/src/main/java/java/lang/StringBuilder.java
@@ -624,7 +624,7 @@
* the inclusive begin index.
* @param end
* the exclusive end index.
- * @param str
+ * @param string
* the replacement string.
* @return this builder.
* @throws StringIndexOutOfBoundsException
@@ -633,8 +633,8 @@
* @throws NullPointerException
* if {@code str} is {@code null}.
*/
- public StringBuilder replace(int start, int end, String str) {
- replace0(start, end, str);
+ public StringBuilder replace(int start, int end, String string) {
+ replace0(start, end, string);
return this;
}
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index 24ebf6b..df84c61 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -34,6 +34,7 @@
import dalvik.system.VMRuntime;
import dalvik.system.VMStack;
+import java.io.BufferedInputStream;
import java.io.Console;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -83,10 +84,9 @@
private static Properties systemProperties;
static {
- // TODO: all three streams are buffered in Harmony.
err = new PrintStream(new FileOutputStream(FileDescriptor.err));
out = new PrintStream(new FileOutputStream(FileDescriptor.out));
- in = new FileInputStream(FileDescriptor.in);
+ in = new BufferedInputStream(new FileInputStream(FileDescriptor.in));
lineSeparator = System.getProperty("line.separator");
}
@@ -455,7 +455,7 @@
*/
public static String clearProperty(String key) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
if (key.isEmpty()) {
throw new IllegalArgumentException();
@@ -679,7 +679,7 @@
private String toNonNullString(Object o) {
if (o == null) {
- throw new NullPointerException();
+ throw new NullPointerException("o == null");
}
return (String) o;
}
diff --git a/luni/src/main/java/java/lang/Thread.java b/luni/src/main/java/java/lang/Thread.java
index a61f669..210b90c 100644
--- a/luni/src/main/java/java/lang/Thread.java
+++ b/luni/src/main/java/java/lang/Thread.java
@@ -232,7 +232,7 @@
*/
public Thread(Runnable runnable, String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(null, runnable, threadName, 0);
@@ -252,7 +252,7 @@
*/
public Thread(String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(null, null, threadName, 0);
@@ -296,7 +296,7 @@
*/
public Thread(ThreadGroup group, Runnable runnable, String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(group, runnable, threadName, 0);
@@ -317,7 +317,7 @@
*/
public Thread(ThreadGroup group, String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(group, null, threadName, 0);
@@ -346,7 +346,7 @@
*/
public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
create(group, runnable, threadName, stackSize);
}
@@ -943,7 +943,7 @@
*/
public final void setName(String threadName) {
if (threadName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("threadName == null");
}
name = threadName;
diff --git a/luni/src/main/java/java/lang/Throwable.java b/luni/src/main/java/java/lang/Throwable.java
index b561832..b20b882 100644
--- a/luni/src/main/java/java/lang/Throwable.java
+++ b/luni/src/main/java/java/lang/Throwable.java
@@ -23,6 +23,7 @@
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import libcore.util.EmptyArray;
@@ -63,13 +64,13 @@
* Throwables suppressed by this throwable. Null when suppressed exceptions
* are disabled.
*/
- private List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
+ private List<Throwable> suppressedExceptions = Collections.emptyList();
/**
* An intermediate representation of the stack trace. This field may
* be accessed by the VM; do not rename.
*/
- private volatile Object stackState;
+ private transient volatile Object stackState;
/**
* A fully-expanded representation of the stack trace.
@@ -218,9 +219,9 @@
*/
public void setStackTrace(StackTraceElement[] trace) {
StackTraceElement[] newTrace = trace.clone();
- for (StackTraceElement element : newTrace) {
- if (element == null) {
- throw new NullPointerException();
+ for (int i = 0; i < newTrace.length; i++) {
+ if (newTrace[i] == null) {
+ throw new NullPointerException("trace[" + i + "] == null");
}
}
stackTrace = newTrace;
@@ -412,12 +413,17 @@
*/
public final void addSuppressed(Throwable throwable) {
if (throwable == this) {
- throw new IllegalArgumentException("suppressed == this");
+ throw new IllegalArgumentException("throwable == this");
}
if (throwable == null) {
- throw new NullPointerException("suppressed == null");
+ throw new NullPointerException("throwable == null");
}
if (suppressedExceptions != null) {
+ // suppressed exceptions are enabled
+ if (suppressedExceptions.isEmpty()) {
+ // ensure we have somewhere to place suppressed exceptions
+ suppressedExceptions = new ArrayList<Throwable>(1);
+ }
suppressedExceptions.add(throwable);
}
}
@@ -429,7 +435,7 @@
* @hide 1.7
*/
public final Throwable[] getSuppressed() {
- return (suppressedExceptions != null)
+ return (suppressedExceptions != null && !suppressedExceptions.isEmpty())
? suppressedExceptions.toArray(new Throwable[suppressedExceptions.size()])
: EmptyArray.THROWABLE;
}
diff --git a/luni/src/main/java/java/lang/ref/FinalizerReference.java b/luni/src/main/java/java/lang/ref/FinalizerReference.java
index aadf1f6..14eaae4 100644
--- a/luni/src/main/java/java/lang/ref/FinalizerReference.java
+++ b/luni/src/main/java/java/lang/ref/FinalizerReference.java
@@ -20,33 +20,39 @@
* @hide
*/
public final class FinalizerReference<T> extends Reference<T> {
+ // This queue contains those objects eligible for finalization.
public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
- private static FinalizerReference head = null;
+ // Guards the list (not the queue).
+ private static final Object LIST_LOCK = new Object();
+ // This list contains a FinalizerReference for every finalizable object in the heap.
+ // Objects in this list may or may not be eligible for finalization yet.
+ private static FinalizerReference<?> head = null;
+
+ // The links used to construct the list.
+ private FinalizerReference<?> prev;
+ private FinalizerReference<?> next;
+
+ // When the GC wants something finalized, it moves it from the 'referent' field to
+ // the 'zombie' field instead.
private T zombie;
- private FinalizerReference prev;
-
- private FinalizerReference next;
-
public FinalizerReference(T r, ReferenceQueue<? super T> q) {
super(r, q);
}
- @Override
- public T get() {
+ @Override public T get() {
return zombie;
}
- @Override
- public void clear() {
+ @Override public void clear() {
zombie = null;
}
- static void add(Object referent) {
+ public static void add(Object referent) {
FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);
- synchronized (FinalizerReference.class) {
+ synchronized (LIST_LOCK) {
reference.prev = null;
reference.next = head;
if (head != null) {
@@ -56,10 +62,10 @@
}
}
- public static void remove(FinalizerReference reference) {
- synchronized (FinalizerReference.class) {
- FinalizerReference next = reference.next;
- FinalizerReference prev = reference.prev;
+ public static void remove(FinalizerReference<?> reference) {
+ synchronized (LIST_LOCK) {
+ FinalizerReference<?> next = reference.next;
+ FinalizerReference<?> prev = reference.prev;
reference.next = null;
reference.prev = null;
if (prev != null) {
@@ -74,30 +80,50 @@
}
/**
- * Returns once all currently-enqueued references have been finalized.
+ * Waits for all currently-enqueued references to be finalized.
*/
public static void finalizeAllEnqueued() throws InterruptedException {
Sentinel sentinel = new Sentinel();
- FinalizerReference<Object> reference = new FinalizerReference<Object>(null, queue);
- reference.zombie = sentinel;
- reference.enqueueInternal();
+ enqueueSentinelReference(sentinel);
sentinel.awaitFinalization();
}
+ private static void enqueueSentinelReference(Sentinel sentinel) {
+ synchronized (LIST_LOCK) {
+ // When a finalizable object is allocated, a FinalizerReference is added to the list.
+ // We search the list for that FinalizerReference (it should be at or near the head),
+ // and then put it on the queue so that it can be finalized.
+ for (FinalizerReference<?> r = head; r != null; r = r.next) {
+ if (r.referent == sentinel) {
+ FinalizerReference<Sentinel> sentinelReference = (FinalizerReference<Sentinel>) r;
+ sentinelReference.referent = null;
+ sentinelReference.zombie = sentinel;
+ sentinelReference.enqueueInternal();
+ return;
+ }
+ }
+ }
+ // We just created a finalizable object and still hold a reference to it.
+ // It must be on the list.
+ throw new AssertionError("newly-created live Sentinel not on list!");
+ }
+
/**
* A marker object that we can immediately enqueue. When this object's
* finalize() method is called, we know all previously-enqueued finalizable
* references have been finalized.
- *
- * <p>Each instance of this class will be finalized twice as it is enqueued
- * directly and by the garbage collector.
*/
private static class Sentinel {
boolean finalized = false;
+
@Override protected synchronized void finalize() throws Throwable {
+ if (finalized) {
+ throw new AssertionError();
+ }
finalized = true;
notifyAll();
}
+
synchronized void awaitFinalization() throws InterruptedException {
while (!finalized) {
wait();
diff --git a/luni/src/main/java/java/lang/ref/Reference.java b/luni/src/main/java/java/lang/ref/Reference.java
index 85fbb04..9cf49a7 100644
--- a/luni/src/main/java/java/lang/ref/Reference.java
+++ b/luni/src/main/java/java/lang/ref/Reference.java
@@ -55,8 +55,7 @@
* VM requirement: this field <em>must</em> be called "queue"
* and be a java.lang.ref.ReferenceQueue.
*/
- @SuppressWarnings("unchecked")
- volatile ReferenceQueue queue;
+ volatile ReferenceQueue<? super T> queue;
/**
* Used internally by java.lang.ref.ReferenceQueue.
@@ -82,7 +81,7 @@
Reference() {
}
- Reference(T r, ReferenceQueue q) {
+ Reference(T r, ReferenceQueue<? super T> q) {
referent = r;
queue = q;
}
diff --git a/luni/src/main/java/java/lang/ref/ReferenceQueue.java b/luni/src/main/java/java/lang/ref/ReferenceQueue.java
index 6c9b4d5..2b8089c 100644
--- a/luni/src/main/java/java/lang/ref/ReferenceQueue.java
+++ b/luni/src/main/java/java/lang/ref/ReferenceQueue.java
@@ -131,8 +131,6 @@
*
* @param reference
* reference object to be enqueued.
- * @return boolean true if reference is enqueued. false if reference failed
- * to enqueue.
*/
synchronized void enqueue(Reference<? extends T> reference) {
if (head == null) {
@@ -145,7 +143,7 @@
}
/** @hide */
- public static Reference unenqueued = null;
+ public static Reference<?> unenqueued = null;
static void add(Reference<?> list) {
synchronized (ReferenceQueue.class) {
diff --git a/luni/src/main/java/java/lang/reflect/GenericArrayType.java b/luni/src/main/java/java/lang/reflect/GenericArrayType.java
index 6344019..fc03f78 100644
--- a/luni/src/main/java/java/lang/reflect/GenericArrayType.java
+++ b/luni/src/main/java/java/lang/reflect/GenericArrayType.java
@@ -36,4 +36,4 @@
* instantiated for some reason
*/
Type getGenericComponentType();
-}
\ No newline at end of file
+}
diff --git a/luni/src/main/java/java/lang/reflect/Proxy.java b/luni/src/main/java/java/lang/reflect/Proxy.java
index a24514d..3b10887 100644
--- a/luni/src/main/java/java/lang/reflect/Proxy.java
+++ b/luni/src/main/java/java/lang/reflect/Proxy.java
@@ -89,13 +89,13 @@
Class<?>... interfaces) throws IllegalArgumentException {
// check that interfaces are a valid array of visible interfaces
if (interfaces == null) {
- throw new NullPointerException();
+ throw new NullPointerException("interfaces == null");
}
String commonPackageName = null;
for (int i = 0, length = interfaces.length; i < length; i++) {
Class<?> next = interfaces[i];
if (next == null) {
- throw new NullPointerException();
+ throw new NullPointerException("interfaces[" + i + "] == null");
}
String name = next.getName();
if (!next.isInterface()) {
@@ -206,7 +206,7 @@
Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {
if (h == null) {
- throw new NullPointerException();
+ throw new NullPointerException("h == null");
}
try {
return getProxyClass(loader, interfaces).getConstructor(
@@ -241,7 +241,7 @@
*/
public static boolean isProxyClass(Class<?> cl) {
if (cl == null) {
- throw new NullPointerException();
+ throw new NullPointerException("cl == null");
}
synchronized (proxyCache) {
return proxyCache.containsKey(cl);
diff --git a/luni/src/main/java/java/math/BigDecimal.java b/luni/src/main/java/java/math/BigDecimal.java
index 3a5f3cd..335e3bc 100644
--- a/luni/src/main/java/java/math/BigDecimal.java
+++ b/luni/src/main/java/java/math/BigDecimal.java
@@ -276,7 +276,7 @@
long newScale; // the new scale
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("in == null");
}
if ((last >= in.length) || (offset < 0) || (len <= 0) || (last < 0)) {
throw new NumberFormatException("Bad offset/length: offset=" + offset +
@@ -601,7 +601,7 @@
*/
public BigDecimal(BigInteger unscaledVal, int scale) {
if (unscaledVal == null) {
- throw new NullPointerException();
+ throw new NullPointerException("unscaledVal == null");
}
this.scale = scale;
setUnscaledValue(unscaledVal);
@@ -1059,7 +1059,7 @@
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {
// Let be: this = [u1,s1] and divisor = [u2,s2]
if (roundingMode == null) {
- throw new NullPointerException();
+ throw new NullPointerException("roundingMode == null");
}
if (divisor.isZero()) {
throw new ArithmeticException("Division by zero");
@@ -1916,7 +1916,7 @@
*/
public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
if (roundingMode == null) {
- throw new NullPointerException();
+ throw new NullPointerException("roundingMode == null");
}
long diffScale = newScale - (long)scale;
// Let be: 'this' = [u,s]
diff --git a/luni/src/main/java/java/math/BigInt.java b/luni/src/main/java/java/math/BigInt.java
index 1768676..614dbb4 100644
--- a/luni/src/main/java/java/math/BigInt.java
+++ b/luni/src/main/java/java/math/BigInt.java
@@ -153,7 +153,7 @@
*/
String checkString(String s, int base) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
// A valid big integer consists of an optional '-' or '+' followed by
// one or more digit characters appropriate to the given base,
diff --git a/luni/src/main/java/java/net/CookieStore.java b/luni/src/main/java/java/net/CookieStore.java
index d09b7e8..619d65c 100644
--- a/luni/src/main/java/java/net/CookieStore.java
+++ b/luni/src/main/java/java/net/CookieStore.java
@@ -100,4 +100,4 @@
* @return true if any cookies were removed as a result of this call.
*/
boolean removeAll();
-}
\ No newline at end of file
+}
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index 2b468fa..c01f3af 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -244,7 +244,7 @@
checkOpen();
ensureBound();
if (pack == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pack == null");
}
if (pendingConnectException != null) {
throw new SocketException("Pending connect failure", pendingConnectException);
@@ -295,7 +295,7 @@
*/
public void setNetworkInterface(NetworkInterface netInterface) throws SocketException {
if (netInterface == null) {
- throw new NullPointerException("networkInterface == null");
+ throw new NullPointerException("netInterface == null");
}
try {
Libcore.os.setsockoptIfreq(impl.fd, SOL_SOCKET, SO_BINDTODEVICE, netInterface.getName());
@@ -374,7 +374,7 @@
*/
protected DatagramSocket(DatagramSocketImpl socketImpl) {
if (socketImpl == null) {
- throw new NullPointerException();
+ throw new NullPointerException("socketImpl == null");
}
impl = socketImpl;
}
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java
index e06b811..ad81f32 100644
--- a/luni/src/main/java/java/net/NetworkInterface.java
+++ b/luni/src/main/java/java/net/NetworkInterface.java
@@ -122,7 +122,9 @@
private static void collectIpv6Addresses(String interfaceName, int interfaceIndex,
List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) throws SocketException {
- // Format of /proc/net/if_inet6 (all numeric fields are implicit hex).
+ // Format of /proc/net/if_inet6.
+ // All numeric fields are implicit hex,
+ // but not necessarily two-digit (http://code.google.com/p/android/issues/detail?id=34022).
// 1. IPv6 address
// 2. interface index
// 3. prefix length
@@ -130,6 +132,7 @@
// 5. flags
// 6. interface name
// "00000000000000000000000000000001 01 80 10 80 lo"
+ // "fe800000000000000000000000000000 407 40 20 80 wlan0"
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader("/proc/net/if_inet6"));
@@ -139,13 +142,22 @@
if (!line.endsWith(suffix)) {
continue;
}
+
+ // Extract the IPv6 address.
byte[] addressBytes = new byte[16];
for (int i = 0; i < addressBytes.length; ++i) {
addressBytes[i] = (byte) Integer.parseInt(line.substring(2*i, 2*i + 2), 16);
}
- short prefixLength = Short.parseShort(line.substring(36, 38), 16);
- Inet6Address inet6Address = new Inet6Address(addressBytes, null, interfaceIndex);
+ // Extract the prefix length.
+ // Skip the IPv6 address and its trailing space.
+ int prefixLengthStart = 32 + 1;
+ // Skip the interface index and its trailing space.
+ prefixLengthStart = line.indexOf(' ', prefixLengthStart) + 1;
+ int prefixLengthEnd = line.indexOf(' ', prefixLengthStart);
+ short prefixLength = Short.parseShort(line.substring(prefixLengthStart, prefixLengthEnd), 16);
+
+ Inet6Address inet6Address = new Inet6Address(addressBytes, null, interfaceIndex);
addresses.add(inet6Address);
interfaceAddresses.add(new InterfaceAddress(inet6Address, prefixLength));
}
diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
index 5d01469..3226527 100644
--- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -216,6 +216,8 @@
Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
} catch (ErrnoException errnoException) {
throw new AssertionError(errnoException);
+ } catch (SocketException ignored) {
+ // Thrown if the socket has already been closed, but this method can't throw anything.
}
connectedPort = -1;
connectedAddress = null;
diff --git a/luni/src/main/java/java/net/URISyntaxException.java b/luni/src/main/java/java/net/URISyntaxException.java
index e08f444..957ea31 100644
--- a/luni/src/main/java/java/net/URISyntaxException.java
+++ b/luni/src/main/java/java/net/URISyntaxException.java
@@ -49,8 +49,10 @@
public URISyntaxException(String input, String reason, int index) {
super(reason);
- if (input == null || reason == null) {
- throw new NullPointerException();
+ if (input == null) {
+ throw new NullPointerException("input == null");
+ } else if (reason == null) {
+ throw new NullPointerException("reason == null");
}
if (index < -1) {
@@ -76,8 +78,10 @@
public URISyntaxException(String input, String reason) {
super(reason);
- if (input == null || reason == null) {
- throw new NullPointerException();
+ if (input == null) {
+ throw new NullPointerException("input == null");
+ } else if (reason == null) {
+ throw new NullPointerException("reason == null");
}
this.input = input;
diff --git a/luni/src/main/java/java/net/URLClassLoader.java b/luni/src/main/java/java/net/URLClassLoader.java
index 1c8bc43..efb7531 100644
--- a/luni/src/main/java/java/net/URLClassLoader.java
+++ b/luni/src/main/java/java/net/URLClassLoader.java
@@ -822,7 +822,7 @@
while (!searchList.isEmpty()) {
URL nextCandidate = searchList.remove(0);
if (nextCandidate == null) {
- throw new NullPointerException("A URL is null");
+ throw new NullPointerException("nextCandidate == null");
}
if (!handlerMap.containsKey(nextCandidate)) {
URLHandler result;
diff --git a/luni/src/main/java/java/net/URLConnection.java b/luni/src/main/java/java/net/URLConnection.java
index c832bfb..18a264e 100644
--- a/luni/src/main/java/java/net/URLConnection.java
+++ b/luni/src/main/java/java/net/URLConnection.java
@@ -948,11 +948,12 @@
}
/**
- * Sets the maximum time to wait for a connect to complete before giving up.
+ * Sets the maximum time in milliseconds to wait while connecting.
* Connecting to a server will fail with a {@link SocketTimeoutException} if
* the timeout elapses before a connection is established. The default value
- * of {@code 0} disables connect timeouts; connect attempts may wait
- * indefinitely.
+ * of {@code 0} causes us to do a blocking connect. This does not mean we
+ * will never time out, but it probably means you'll get a TCP timeout
+ * after several minutes.
*
* <p><strong>Warning:</strong> if the hostname resolves to multiple IP
* addresses, this client will try each in <a
@@ -961,7 +962,7 @@
* elapse before the connect attempt throws an exception. Host names that
* support both IPv6 and IPv4 always have at least 2 IP addresses.
*
- * @param timeoutMillis the connect timeout in milliseconds. Non-negative.
+ * @throws IllegalArgumentException if {@code timeoutMillis < 0}.
*/
public void setConnectTimeout(int timeoutMillis) {
if (timeoutMillis < 0) {
@@ -971,8 +972,7 @@
}
/**
- * Returns the connect timeout in milliseconds, or {@code 0} if connect
- * attempts never timeout.
+ * Returns the connect timeout in milliseconds. (See {#setConnectTimeout}.)
*/
public int getConnectTimeout() {
return connectTimeout;
diff --git a/luni/src/main/java/java/nio/Buffer.java b/luni/src/main/java/java/nio/Buffer.java
index c3840a5..b90744b 100644
--- a/luni/src/main/java/java/nio/Buffer.java
+++ b/luni/src/main/java/java/nio/Buffer.java
@@ -271,7 +271,7 @@
final void checkWritable() {
if (isReadOnly()) {
- throw new IllegalArgumentException("read-only buffer");
+ throw new IllegalArgumentException("Read-only buffer");
}
}
diff --git a/luni/src/main/java/java/nio/ByteBuffer.java b/luni/src/main/java/java/nio/ByteBuffer.java
index ef725c1..6a3624a 100644
--- a/luni/src/main/java/java/nio/ByteBuffer.java
+++ b/luni/src/main/java/java/nio/ByteBuffer.java
@@ -47,7 +47,7 @@
*/
public static ByteBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteHeapByteBuffer(capacity);
}
@@ -63,7 +63,7 @@
*/
public static ByteBuffer allocateDirect(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteDirectByteBuffer(capacity);
}
diff --git a/luni/src/main/java/java/nio/CharBuffer.java b/luni/src/main/java/java/nio/CharBuffer.java
index 03bac04..6429ae2 100644
--- a/luni/src/main/java/java/nio/CharBuffer.java
+++ b/luni/src/main/java/java/nio/CharBuffer.java
@@ -49,7 +49,7 @@
*/
public static CharBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteCharArrayBuffer(capacity);
}
@@ -500,7 +500,7 @@
*/
public CharBuffer put(CharBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
@@ -734,7 +734,7 @@
if (remaining == 0) {
return -1;
}
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("target == this");
}
if (remaining == 0) {
return limit > 0 && target.remaining() == 0 ? 0 : -1;
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index a0e064d..4d2fc5a 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -448,7 +448,7 @@
*/
private void checkNotNull(ByteBuffer source) {
if (source == null) {
- throw new NullPointerException();
+ throw new NullPointerException("source == null");
}
}
diff --git a/luni/src/main/java/java/nio/DoubleBuffer.java b/luni/src/main/java/java/nio/DoubleBuffer.java
index c495592..8d90f89 100644
--- a/luni/src/main/java/java/nio/DoubleBuffer.java
+++ b/luni/src/main/java/java/nio/DoubleBuffer.java
@@ -47,7 +47,7 @@
*/
public static DoubleBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteDoubleArrayBuffer(capacity);
}
@@ -438,7 +438,7 @@
*/
public DoubleBuffer put(DoubleBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java
index 76bad51..075243a 100644
--- a/luni/src/main/java/java/nio/FileChannelImpl.java
+++ b/luni/src/main/java/java/nio/FileChannelImpl.java
@@ -442,7 +442,7 @@
public FileChannel truncate(long size) throws IOException {
checkOpen();
if (size < 0) {
- throw new IllegalArgumentException("size: " + size);
+ throw new IllegalArgumentException("size < 0: " + size);
}
checkWritable();
if (size < size()) {
@@ -457,7 +457,7 @@
public int write(ByteBuffer buffer, long position) throws IOException {
if (position < 0) {
- throw new IllegalArgumentException("position: " + position);
+ throw new IllegalArgumentException("position < 0: " + position);
}
return writeImpl(buffer, position);
}
diff --git a/luni/src/main/java/java/nio/FloatBuffer.java b/luni/src/main/java/java/nio/FloatBuffer.java
index ec361d6..814eb53 100644
--- a/luni/src/main/java/java/nio/FloatBuffer.java
+++ b/luni/src/main/java/java/nio/FloatBuffer.java
@@ -46,7 +46,7 @@
*/
public static FloatBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteFloatArrayBuffer(capacity);
}
@@ -437,7 +437,7 @@
*/
public FloatBuffer put(FloatBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/IntBuffer.java b/luni/src/main/java/java/nio/IntBuffer.java
index 9cc05ff..0ff758a 100644
--- a/luni/src/main/java/java/nio/IntBuffer.java
+++ b/luni/src/main/java/java/nio/IntBuffer.java
@@ -44,7 +44,7 @@
*/
public static IntBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteIntArrayBuffer(capacity);
}
@@ -423,7 +423,7 @@
*/
public IntBuffer put(IntBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/LongBuffer.java b/luni/src/main/java/java/nio/LongBuffer.java
index 27edd2e..1254ddb 100644
--- a/luni/src/main/java/java/nio/LongBuffer.java
+++ b/luni/src/main/java/java/nio/LongBuffer.java
@@ -46,7 +46,7 @@
*/
public static LongBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteLongArrayBuffer(capacity);
}
@@ -427,7 +427,7 @@
*/
public LongBuffer put(LongBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/MappedByteBuffer.java b/luni/src/main/java/java/nio/MappedByteBuffer.java
index 39c4986..0e8bf09 100644
--- a/luni/src/main/java/java/nio/MappedByteBuffer.java
+++ b/luni/src/main/java/java/nio/MappedByteBuffer.java
@@ -45,7 +45,7 @@
MappedByteBuffer(ByteBuffer directBuffer) {
super(directBuffer.capacity, directBuffer.block);
if (!directBuffer.isDirect()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("directBuffer is not a direct buffer: " + directBuffer);
}
this.wrapped = (DirectByteBuffer) directBuffer;
this.mapMode = null;
diff --git a/luni/src/main/java/java/nio/NIOAccess.java b/luni/src/main/java/java/nio/NIOAccess.java
index 361a37f..36c41cf 100644
--- a/luni/src/main/java/java/nio/NIOAccess.java
+++ b/luni/src/main/java/java/nio/NIOAccess.java
@@ -28,7 +28,7 @@
* different than what the Harmony implementation calls a "base
* address."
*
- * @param Buffer b the Buffer to be queried
+ * @param b the Buffer to be queried
* @return the native pointer to the Buffer's data at its current
* position, or 0 if there is none
*/
@@ -44,7 +44,7 @@
* Returns the underlying Java array containing the data of the
* given Buffer, or null if the Buffer is not backed by a Java array.
*
- * @param Buffer b the Buffer to be queried
+ * @param b the Buffer to be queried
* @return the Java array containing the Buffer's data, or null if
* there is none
*/
@@ -55,13 +55,14 @@
/**
* Returns the offset in bytes from the start of the underlying
* Java array object containing the data of the given Buffer to
- * the actual start of the data. This method is only meaningful if
- * getBaseArray() returns non-null.
+ * the actual start of the data. The start of the data takes into
+ * account the Buffer's current position. This method is only
+ * meaningful if getBaseArray() returns non-null.
*
- * @param Buffer b the Buffer to be queried
+ * @param b the Buffer to be queried
* @return the data offset in bytes to the start of this Buffer's data
*/
static int getBaseArrayOffset(Buffer b) {
- return b.hasArray() ? (b.arrayOffset() << b._elementSizeShift) : 0;
+ return b.hasArray() ? ((b.arrayOffset() + b.position) << b._elementSizeShift) : 0;
}
}
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index 05a6497..02fdf54 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -150,7 +150,7 @@
@Override public int select(long timeout) throws IOException {
if (timeout < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("timeout < 0: " + timeout);
}
// Our timeout is interpreted differently to Unix's --- 0 means block. See selectNow.
return selectInternal((timeout == 0) ? -1 : timeout);
diff --git a/luni/src/main/java/java/nio/ShortBuffer.java b/luni/src/main/java/java/nio/ShortBuffer.java
index 052cf6b..d12a49e 100644
--- a/luni/src/main/java/java/nio/ShortBuffer.java
+++ b/luni/src/main/java/java/nio/ShortBuffer.java
@@ -46,7 +46,7 @@
*/
public static ShortBuffer allocate(int capacity) {
if (capacity < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("capacity < 0: " + capacity);
}
return new ReadWriteShortArrayBuffer(capacity);
}
@@ -426,7 +426,7 @@
*/
public ShortBuffer put(ShortBuffer src) {
if (src == this) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("src == this");
}
if (src.remaining() > remaining()) {
throw new BufferOverflowException();
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index 9b26812..ff2c157 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -318,7 +318,7 @@
@Override
public int write(ByteBuffer src) throws IOException {
if (src == null) {
- throw new NullPointerException();
+ throw new NullPointerException("src == null");
}
checkOpenConnected();
if (!src.hasRemaining()) {
@@ -412,7 +412,7 @@
*/
static InetSocketAddress validateAddress(SocketAddress socketAddress) {
if (socketAddress == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("socketAddress == null");
}
if (!(socketAddress instanceof InetSocketAddress)) {
throw new UnsupportedAddressTypeException();
diff --git a/luni/src/main/java/java/nio/channels/Channels.java b/luni/src/main/java/java/nio/channels/Channels.java
index 3af1465..b59eeac 100644
--- a/luni/src/main/java/java/nio/channels/Channels.java
+++ b/luni/src/main/java/java/nio/channels/Channels.java
@@ -150,7 +150,7 @@
public static Reader newReader(ReadableByteChannel channel,
String charsetName) {
if (charsetName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("charsetName == null");
}
return newReader(channel, Charset.forName(charsetName).newDecoder(), -1);
}
@@ -193,7 +193,7 @@
public static Writer newWriter(WritableByteChannel channel,
String charsetName) {
if (charsetName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("charsetName == null");
}
return newWriter(channel, Charset.forName(charsetName).newEncoder(), -1);
}
@@ -207,7 +207,7 @@
ChannelInputStream(ReadableByteChannel channel) {
if (channel == null) {
- throw new NullPointerException();
+ throw new NullPointerException("channel == null");
}
this.channel = channel;
}
@@ -247,7 +247,7 @@
ChannelOutputStream(WritableByteChannel channel) {
if (channel == null) {
- throw new NullPointerException();
+ throw new NullPointerException("channel == null");
}
this.channel = channel;
}
@@ -289,7 +289,7 @@
InputStreamChannel(InputStream inputStream) {
if (inputStream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("inputStream == null");
}
this.inputStream = inputStream;
}
@@ -328,7 +328,7 @@
OutputStreamChannel(OutputStream outputStream) {
if (outputStream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("outputStream == null");
}
this.outputStream = outputStream;
}
diff --git a/luni/src/main/java/java/nio/channels/FileLock.java b/luni/src/main/java/java/nio/channels/FileLock.java
index 0916be0..4cdcc27 100644
--- a/luni/src/main/java/java/nio/channels/FileLock.java
+++ b/luni/src/main/java/java/nio/channels/FileLock.java
@@ -98,7 +98,7 @@
*/
protected FileLock(FileChannel channel, long position, long size, boolean shared) {
if (position < 0 || size < 0 || position + size < 0) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("position=" + position + " size=" + size);
}
this.channel = channel;
this.position = position;
diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoder.java b/luni/src/main/java/java/nio/charset/CharsetDecoder.java
index f67dbbc..1559db4 100644
--- a/luni/src/main/java/java/nio/charset/CharsetDecoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetDecoder.java
@@ -590,7 +590,7 @@
*/
public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) {
if (newAction == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("newAction == null");
}
malformedInputAction = newAction;
implOnMalformedInput(newAction);
@@ -612,7 +612,7 @@
*/
public final CharsetDecoder onUnmappableCharacter(CodingErrorAction newAction) {
if (newAction == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("newAction == null");
}
unmappableCharacterAction = newAction;
implOnUnmappableCharacter(newAction);
diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
index 3ad46d7..8b32efa 100644
--- a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
+++ b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java
@@ -16,7 +16,7 @@
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
-import libcore.icu.ErrorCode;
+import libcore.icu.ICU;
import libcore.icu.NativeConverter;
import libcore.util.EmptyArray;
@@ -46,7 +46,6 @@
// is inherently thread-unsafe so we don't have to worry about synchronization.
private int inEnd;
private int outEnd;
- private int ec;
public static CharsetDecoderICU newInstance(Charset cs, String icuCanonicalName) {
// This complexity is necessary to ensure that even if the constructor, superclass
@@ -84,10 +83,7 @@
}
private void updateCallback() {
- ec = NativeConverter.setCallbackDecode(converterHandle, this);
- if (ErrorCode.isFailure(ec)) {
- throw ErrorCode.throwException(ec);
- }
+ NativeConverter.setCallbackDecode(converterHandle, this);
}
@Override protected void implReset() {
@@ -99,7 +95,6 @@
input = null;
allocatedInput = null;
allocatedOutput = null;
- ec = 0;
inEnd = 0;
outEnd = 0;
}
@@ -114,16 +109,14 @@
data[OUTPUT_OFFSET] = getArray(out);
data[INVALID_BYTES] = 0; // Make sure we don't see earlier errors.
- ec = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, true);
- if (ErrorCode.isFailure(ec)) {
- if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) {
+ int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, true);
+ if (ICU.U_FAILURE(error)) {
+ if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
return CoderResult.OVERFLOW;
- } else if (ec == ErrorCode.U_TRUNCATED_CHAR_FOUND) {
+ } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) {
if (data[INPUT_OFFSET] > 0) {
return CoderResult.malformedForLength(data[INPUT_OFFSET]);
}
- } else {
- throw ErrorCode.throwException(ec);
}
}
return CoderResult.UNDERFLOW;
@@ -142,13 +135,17 @@
data[OUTPUT_OFFSET]= getArray(out);
try {
- ec = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, false);
- if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) {
- return CoderResult.OVERFLOW;
- } else if (ec == ErrorCode.U_INVALID_CHAR_FOUND) {
- return CoderResult.unmappableForLength(data[INVALID_BYTES]);
- } else if (ec == ErrorCode.U_ILLEGAL_CHAR_FOUND) {
- return CoderResult.malformedForLength(data[INVALID_BYTES]);
+ int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, false);
+ if (ICU.U_FAILURE(error)) {
+ if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
+ return CoderResult.OVERFLOW;
+ } else if (error == ICU.U_INVALID_CHAR_FOUND) {
+ return CoderResult.unmappableForLength(data[INVALID_BYTES]);
+ } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) {
+ return CoderResult.malformedForLength(data[INVALID_BYTES]);
+ } else {
+ throw new AssertionError(error);
+ }
}
// Decoding succeeded: give us more data.
return CoderResult.UNDERFLOW;
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
index 28b2cb8..8f02f96 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
@@ -706,11 +706,11 @@
throw new IllegalArgumentException("replacement.length == 0");
}
if (replacement.length > maxBytesPerChar()) {
- throw new IllegalArgumentException("replacement length > maxBytesPerChar: " +
+ throw new IllegalArgumentException("replacement.length > maxBytesPerChar: " +
replacement.length + " > " + maxBytesPerChar());
}
if (!isLegalReplacement(replacement)) {
- throw new IllegalArgumentException("bad replacement: " + Arrays.toString(replacement));
+ throw new IllegalArgumentException("Bad replacement: " + Arrays.toString(replacement));
}
// It seems like a bug, but the RI doesn't clone, and we have tests that check we don't.
this.replacementBytes = replacement;
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java b/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
index cf071ca..76807b0 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java
@@ -18,7 +18,7 @@
import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;
-import libcore.icu.ErrorCode;
+import libcore.icu.ICU;
import libcore.icu.NativeConverter;
import libcore.util.EmptyArray;
@@ -61,7 +61,6 @@
// is inherently thread-unsafe so we don't have to worry about synchronization.
private int inEnd;
private int outEnd;
- private int ec;
public static CharsetEncoderICU newInstance(Charset cs, String icuCanonicalName) {
// This complexity is necessary to ensure that even if the constructor, superclass
@@ -112,10 +111,7 @@
}
private void updateCallback() {
- ec = NativeConverter.setCallbackEncode(converterHandle, this);
- if (ErrorCode.isFailure(ec)) {
- throw ErrorCode.throwException(ec);
- }
+ NativeConverter.setCallbackEncode(converterHandle, this);
}
@Override protected void implReset() {
@@ -127,7 +123,6 @@
input = null;
allocatedInput = null;
allocatedOutput = null;
- ec = 0;
inEnd = 0;
outEnd = 0;
}
@@ -142,16 +137,14 @@
data[OUTPUT_OFFSET] = getArray(out);
data[INVALID_CHARS] = 0; // Make sure we don't see earlier errors.
- ec = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, true);
- if (ErrorCode.isFailure(ec)) {
- if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) {
+ int error = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, true);
+ if (ICU.U_FAILURE(error)) {
+ if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
return CoderResult.OVERFLOW;
- } else if (ec == ErrorCode.U_TRUNCATED_CHAR_FOUND) {
+ } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) {
if (data[INPUT_OFFSET] > 0) {
return CoderResult.malformedForLength(data[INPUT_OFFSET]);
}
- } else {
- throw ErrorCode.throwException(ec);
}
}
return CoderResult.UNDERFLOW;
@@ -171,16 +164,16 @@
data[INVALID_CHARS] = 0; // Make sure we don't see earlier errors.
try {
- ec = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, false);
- if (ErrorCode.isFailure(ec)) {
- if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) {
+ int error = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, false);
+ if (ICU.U_FAILURE(error)) {
+ if (error == ICU.U_BUFFER_OVERFLOW_ERROR) {
return CoderResult.OVERFLOW;
- } else if (ec == ErrorCode.U_INVALID_CHAR_FOUND) {
+ } else if (error == ICU.U_INVALID_CHAR_FOUND) {
return CoderResult.unmappableForLength(data[INVALID_CHARS]);
- } else if (ec == ErrorCode.U_ILLEGAL_CHAR_FOUND) {
+ } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) {
return CoderResult.malformedForLength(data[INVALID_CHARS]);
} else {
- throw new AssertionError("unexpected failure: " + ec);
+ throw new AssertionError(error);
}
}
// Decoding succeeded: give us more data.
diff --git a/luni/src/main/java/java/nio/charset/Charsets.java b/luni/src/main/java/java/nio/charset/Charsets.java
index 1c2425a..826b12a 100644
--- a/luni/src/main/java/java/nio/charset/Charsets.java
+++ b/luni/src/main/java/java/nio/charset/Charsets.java
@@ -25,7 +25,7 @@
*
* @hide internal use only
*/
-public class Charsets {
+public final class Charsets {
/**
* A cheap and type-safe constant for the ISO-8859-1 Charset.
*/
diff --git a/luni/src/main/java/java/nio/charset/CoderResult.java b/luni/src/main/java/java/nio/charset/CoderResult.java
index 221cb32..3cc2673 100644
--- a/luni/src/main/java/java/nio/charset/CoderResult.java
+++ b/luni/src/main/java/java/nio/charset/CoderResult.java
@@ -121,7 +121,7 @@
return r;
}
}
- throw new IllegalArgumentException("Length must be greater than 0; was " + length);
+ throw new IllegalArgumentException("length <= 0: " + length);
}
/**
@@ -149,7 +149,7 @@
return r;
}
}
- throw new IllegalArgumentException("Length must be greater than 0; was " + length);
+ throw new IllegalArgumentException("length <= 0: " + length);
}
/**
diff --git a/luni/src/main/java/java/security/AlgorithmParameterGenerator.java b/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
index daefa3c..3edc167 100644
--- a/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
+++ b/luni/src/main/java/java/security/AlgorithmParameterGenerator.java
@@ -88,7 +88,7 @@
public static AlgorithmParameterGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) sap.spi,
@@ -149,7 +149,7 @@
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) spi, provider,
diff --git a/luni/src/main/java/java/security/AlgorithmParameters.java b/luni/src/main/java/java/security/AlgorithmParameters.java
index 8bbbe1b..073460e 100644
--- a/luni/src/main/java/java/security/AlgorithmParameters.java
+++ b/luni/src/main/java/java/security/AlgorithmParameters.java
@@ -92,7 +92,7 @@
public static AlgorithmParameters getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new AlgorithmParameters((AlgorithmParametersSpi) sap.spi, sap.provider, algorithm);
@@ -120,7 +120,7 @@
String provider) throws NoSuchAlgorithmException,
NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null || provider.isEmpty()");
}
Provider p = Security.getProvider(provider);
if (p == null) {
@@ -148,10 +148,10 @@
public static AlgorithmParameters getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
if (provider == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new AlgorithmParameters((AlgorithmParametersSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/KeyFactory.java b/luni/src/main/java/java/security/KeyFactory.java
index 554885a..8d39003 100644
--- a/luni/src/main/java/java/security/KeyFactory.java
+++ b/luni/src/main/java/java/security/KeyFactory.java
@@ -76,7 +76,7 @@
public static KeyFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new KeyFactory((KeyFactorySpi) sap.spi, sap.provider, algorithm);
@@ -130,7 +130,7 @@
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new KeyFactory((KeyFactorySpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/KeyPairGenerator.java b/luni/src/main/java/java/security/KeyPairGenerator.java
index d5851fa..5c17d79 100644
--- a/luni/src/main/java/java/security/KeyPairGenerator.java
+++ b/luni/src/main/java/java/security/KeyPairGenerator.java
@@ -80,7 +80,7 @@
public static KeyPairGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
Object spi = sap.spi;
@@ -143,7 +143,7 @@
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
if (spi instanceof KeyPairGenerator) {
diff --git a/luni/src/main/java/java/security/KeyStore.java b/luni/src/main/java/java/security/KeyStore.java
index c233a5b..3d856f7 100644
--- a/luni/src/main/java/java/security/KeyStore.java
+++ b/luni/src/main/java/java/security/KeyStore.java
@@ -110,7 +110,7 @@
*/
public static KeyStore getInstance(String type) throws KeyStoreException {
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Engine.SpiAndProvider sap = ENGINE.getInstance(type, null);
@@ -182,7 +182,7 @@
throw new IllegalArgumentException();
}
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
// return KeyStore instance
try {
diff --git a/luni/src/main/java/java/security/KeyStoreSpi.java b/luni/src/main/java/java/security/KeyStoreSpi.java
index 01565f5..5ae9a72 100644
--- a/luni/src/main/java/java/security/KeyStoreSpi.java
+++ b/luni/src/main/java/java/security/KeyStoreSpi.java
@@ -414,14 +414,14 @@
}
char[] passW = null;
- if (protParam instanceof KeyStore.PasswordProtection) {
- try {
- passW = ((KeyStore.PasswordProtection) protParam).getPassword();
- } catch (IllegalStateException ee) {
- throw new KeyStoreException("Password was destroyed", ee);
- }
- } else {
- if (protParam instanceof KeyStore.CallbackHandlerProtection) {
+ if (protParam != null) {
+ if (protParam instanceof KeyStore.PasswordProtection) {
+ try {
+ passW = ((KeyStore.PasswordProtection) protParam).getPassword();
+ } catch (IllegalStateException ee) {
+ throw new KeyStoreException("Password was destroyed", ee);
+ }
+ } else if (protParam instanceof KeyStore.CallbackHandlerProtection) {
try {
passW = getPasswordFromCallBack(protParam);
} catch (Exception e) {
diff --git a/luni/src/main/java/java/security/MessageDigest.java b/luni/src/main/java/java/security/MessageDigest.java
index 3154028..6f1bf21 100644
--- a/luni/src/main/java/java/security/MessageDigest.java
+++ b/luni/src/main/java/java/security/MessageDigest.java
@@ -86,7 +86,7 @@
public static MessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
Object spi = sap.spi;
@@ -152,7 +152,7 @@
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
if (spi instanceof MessageDigest) {
@@ -217,7 +217,7 @@
*/
public void update(byte[] input) {
if (input == null) {
- throw new NullPointerException();
+ throw new NullPointerException("input == null");
}
engineUpdate(input, 0, input.length);
}
diff --git a/luni/src/main/java/java/security/Provider.java b/luni/src/main/java/java/security/Provider.java
index 2de3751..899625a 100644
--- a/luni/src/main/java/java/security/Provider.java
+++ b/luni/src/main/java/java/security/Provider.java
@@ -27,12 +27,13 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import org.apache.harmony.luni.util.TwoKeyHashMap;
import org.apache.harmony.security.fortress.Services;
/**
@@ -57,18 +58,18 @@
// Contains "Service.Algorithm" and Provider.Service classes added using
// putService()
- private transient TwoKeyHashMap<String, String, Service> serviceTable;
+ private transient LinkedHashMap<String, Service> serviceTable;
// Contains "Service.Alias" and Provider.Service classes added using
// putService()
- private transient TwoKeyHashMap<String, String, Service> aliasTable;
+ private transient LinkedHashMap<String, Service> aliasTable;
// Contains "Service.Algorithm" and Provider.Service classes added using
// put()
- private transient TwoKeyHashMap<String, String, Service> propertyServiceTable;
+ private transient LinkedHashMap<String, Service> propertyServiceTable;
// Contains "Service.Alias" and Provider.Service classes added using put()
- private transient TwoKeyHashMap<String, String, Service> propertyAliasTable;
+ private transient LinkedHashMap<String, Service> propertyAliasTable;
// The properties changed via put()
private transient Properties changedProperties;
@@ -406,30 +407,32 @@
*/
public synchronized Provider.Service getService(String type,
String algorithm) {
- if (type == null || algorithm == null) {
- throw new NullPointerException();
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ } else if (algorithm == null) {
+ throw new NullPointerException("algorithm == null");
}
if (type.equals(lastServiceName) && algorithm.equalsIgnoreCase(lastAlgorithm)) {
return returnedService;
}
- String alg = algorithm.toUpperCase(Locale.US);
+ String key = key(type, algorithm);
Object o = null;
if (serviceTable != null) {
- o = serviceTable.get(type, alg);
+ o = serviceTable.get(key);
}
if (o == null && aliasTable != null) {
- o = aliasTable.get(type, alg);
+ o = aliasTable.get(key);
}
if (o == null) {
updatePropertyServiceTable();
}
if (o == null && propertyServiceTable != null) {
- o = propertyServiceTable.get(type, alg);
+ o = propertyServiceTable.get(key);
}
if (o == null && propertyAliasTable != null) {
- o = propertyAliasTable.get(type, alg);
+ o = propertyAliasTable.get(key);
}
if (o != null) {
@@ -454,9 +457,9 @@
return lastServicesSet;
}
if (serviceTable != null) {
- lastServicesSet = new HashSet<Service>(serviceTable.values());
+ lastServicesSet = new LinkedHashSet<Service>(serviceTable.values());
} else {
- lastServicesSet = new HashSet<Service>();
+ lastServicesSet = new LinkedHashSet<Service>();
}
if (propertyServiceTable != null) {
lastServicesSet.addAll(propertyServiceTable.values());
@@ -474,22 +477,22 @@
*/
protected synchronized void putService(Provider.Service s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
if ("Provider".equals(s.getType())) { // Provider service type cannot be added
return;
}
servicesChanged();
if (serviceTable == null) {
- serviceTable = new TwoKeyHashMap<String, String, Service>(128);
+ serviceTable = new LinkedHashMap<String, Service>(128);
}
- serviceTable.put(s.type, s.algorithm.toUpperCase(Locale.US), s);
+ serviceTable.put(key(s.type, s.algorithm), s);
if (s.aliases != null) {
if (aliasTable == null) {
- aliasTable = new TwoKeyHashMap<String, String, Service>(256);
+ aliasTable = new LinkedHashMap<String, Service>(256);
}
for (String alias : s.getAliases()) {
- aliasTable.put(s.type, alias.toUpperCase(Locale.US), s);
+ aliasTable.put(key(s.type, alias), s);
}
}
serviceInfoToProperties(s);
@@ -506,15 +509,15 @@
*/
protected synchronized void removeService(Provider.Service s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
servicesChanged();
if (serviceTable != null) {
- serviceTable.remove(s.type, s.algorithm.toUpperCase(Locale.US));
+ serviceTable.remove(key(s.type, s.algorithm));
}
if (aliasTable != null && s.aliases != null) {
for (String alias: s.getAliases()) {
- aliasTable.remove(s.type, alias.toUpperCase(Locale.US));
+ aliasTable.remove(key(s.type, alias));
}
}
serviceInfoFromProperties(s);
@@ -584,7 +587,7 @@
serviceName = service_alias.substring(0, i);
aliasName = service_alias.substring(i + 1);
if (propertyAliasTable != null) {
- propertyAliasTable.remove(serviceName, aliasName.toUpperCase(Locale.US));
+ propertyAliasTable.remove(key(serviceName, aliasName));
}
if (propertyServiceTable != null) {
for (Iterator<Service> it = propertyServiceTable.values().iterator(); it
@@ -608,12 +611,11 @@
serviceName = k.substring(0, j);
algorithm = k.substring(j + 1);
if (propertyServiceTable != null) {
- Provider.Service ser = propertyServiceTable.remove(serviceName,
- algorithm.toUpperCase(Locale.US));
+ Provider.Service ser = propertyServiceTable.remove(key(serviceName, algorithm));
if (ser != null && propertyAliasTable != null
&& ser.aliases != null) {
for (String alias : ser.aliases) {
- propertyAliasTable.remove(serviceName, alias.toUpperCase(Locale.US));
+ propertyAliasTable.remove(key(serviceName, alias));
}
}
}
@@ -624,7 +626,7 @@
serviceName = k.substring(0, j);
algorithm = k.substring(j + 1, i);
if (propertyServiceTable != null) {
- Object o = propertyServiceTable.get(serviceName, algorithm.toUpperCase(Locale.US));
+ Object o = propertyServiceTable.get(key(serviceName, algorithm));
if (o != null) {
s = (Provider.Service) o;
s.attributes.remove(attribute);
@@ -667,20 +669,20 @@
serviceName = service_alias.substring(0, i);
aliasName = service_alias.substring(i + 1);
algorithm = value;
- String algUp = algorithm.toUpperCase(Locale.US);
+ String propertyServiceTableKey = key(serviceName, algorithm);
Object o = null;
if (propertyServiceTable == null) {
- propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
+ propertyServiceTable = new LinkedHashMap<String, Service>(128);
} else {
- o = propertyServiceTable.get(serviceName, algUp);
+ o = propertyServiceTable.get(propertyServiceTableKey);
}
if (o != null) {
s = (Provider.Service) o;
s.addAlias(aliasName);
if (propertyAliasTable == null) {
- propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256);
+ propertyAliasTable = new LinkedHashMap<String, Service>(256);
}
- propertyAliasTable.put(serviceName, aliasName.toUpperCase(Locale.US), s);
+ propertyAliasTable.put(key(serviceName, aliasName), s);
} else {
String className = (String) changedProperties
.get(serviceName + "." + algorithm);
@@ -689,11 +691,11 @@
l.add(aliasName);
s = new Provider.Service(this, serviceName, algorithm,
className, l, new HashMap<String, String>());
- propertyServiceTable.put(serviceName, algUp, s);
+ propertyServiceTable.put(propertyServiceTableKey, s);
if (propertyAliasTable == null) {
- propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256);
+ propertyAliasTable = new LinkedHashMap<String, Service>(256);
}
- propertyAliasTable.put(serviceName, aliasName.toUpperCase(Locale.US), s);
+ propertyAliasTable.put(key(serviceName, aliasName), s);
}
}
continue;
@@ -706,10 +708,10 @@
if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className>
serviceName = key.substring(0, j);
algorithm = key.substring(j + 1);
- String alg = algorithm.toUpperCase(Locale.US);
+ String propertyServiceTableKey = key(serviceName, algorithm);
Object o = null;
if (propertyServiceTable != null) {
- o = propertyServiceTable.get(serviceName, alg);
+ o = propertyServiceTable.get(propertyServiceTableKey);
}
if (o != null) {
s = (Provider.Service) o;
@@ -719,21 +721,20 @@
value, Collections.<String>emptyList(),
Collections.<String,String>emptyMap());
if (propertyServiceTable == null) {
- propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
+ propertyServiceTable = new LinkedHashMap<String, Service>(128);
}
- propertyServiceTable.put(serviceName, alg, s);
+ propertyServiceTable.put(propertyServiceTableKey, s);
}
} else {
- // <crypto_service>.<algorithm_or_type>
- // <attribute_name>=<attrValue>
+ // <crypto_service>.<algorithm_or_type> <attribute_name>=<attrValue>
serviceName = key.substring(0, j);
algorithm = key.substring(j + 1, i);
String attribute = key.substring(i + 1);
- String alg = algorithm.toUpperCase(Locale.US);
+ String propertyServiceTableKey = key(serviceName, algorithm);
Object o = null;
if (propertyServiceTable != null) {
- o = propertyServiceTable.get(serviceName, alg);
+ o = propertyServiceTable.get(propertyServiceTableKey);
}
if (o != null) {
s = (Provider.Service) o;
@@ -747,9 +748,9 @@
s = new Provider.Service(this, serviceName, algorithm,
className, new ArrayList<String>(), m);
if (propertyServiceTable == null) {
- propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
+ propertyServiceTable = new LinkedHashMap<String, Service>(128);
}
- propertyServiceTable.put(serviceName, alg, s);
+ propertyServiceTable.put(propertyServiceTableKey, s);
}
}
}
@@ -794,6 +795,10 @@
return null;
}
+ private static String key(String type, String algorithm) {
+ return type + '.' + algorithm.toUpperCase(Locale.US);
+ }
+
/**
* {@code Service} represents a service in the Java Security infrastructure.
* Each service describes its type, the algorithm it implements, to which
@@ -849,9 +854,14 @@
*/
public Service(Provider provider, String type, String algorithm,
String className, List<String> aliases, Map<String, String> attributes) {
- if (provider == null || type == null || algorithm == null
- || className == null) {
- throw new NullPointerException();
+ if (provider == null) {
+ throw new NullPointerException("provider == null");
+ } else if (type == null) {
+ throw new NullPointerException("type == null");
+ } else if (algorithm == null) {
+ throw new NullPointerException("algorithm == null");
+ } else if (className == null) {
+ throw new NullPointerException("className == null");
}
this.provider = provider;
this.type = type;
@@ -940,7 +950,7 @@
*/
public final String getAttribute(String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (attributes == null) {
return null;
diff --git a/luni/src/main/java/java/security/SecureRandom.java b/luni/src/main/java/java/security/SecureRandom.java
index 68a2917..6ed631c 100644
--- a/luni/src/main/java/java/security/SecureRandom.java
+++ b/luni/src/main/java/java/security/SecureRandom.java
@@ -88,7 +88,6 @@
*/
public SecureRandom() {
super(0);
- Services.refresh();
Provider.Service service = Services.getSecureRandomService();
if (service == null) {
this.provider = null;
@@ -154,7 +153,7 @@
*/
public static SecureRandom getInstance(String algorithm) throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new SecureRandom((SecureRandomSpi) sap.spi, sap.provider,
@@ -213,7 +212,7 @@
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new SecureRandom((SecureRandomSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/Security.java b/luni/src/main/java/java/security/Security.java
index a4cc6e1..b5bd02a 100644
--- a/luni/src/main/java/java/security/Security.java
+++ b/luni/src/main/java/java/security/Security.java
@@ -227,7 +227,7 @@
*/
public static Provider[] getProviders(String filter) {
if (filter == null) {
- throw new NullPointerException();
+ throw new NullPointerException("filter == null");
}
if (filter.length() == 0) {
throw new InvalidParameterException();
@@ -271,7 +271,7 @@
*/
public static synchronized Provider[] getProviders(Map<String,String> filter) {
if (filter == null) {
- throw new NullPointerException();
+ throw new NullPointerException("filter == null");
}
if (filter.isEmpty()) {
return null;
diff --git a/luni/src/main/java/java/security/Signature.java b/luni/src/main/java/java/security/Signature.java
index d9e1e41..be89654 100644
--- a/luni/src/main/java/java/security/Signature.java
+++ b/luni/src/main/java/java/security/Signature.java
@@ -99,7 +99,7 @@
public static Signature getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
Object spi = sap.spi;
@@ -134,7 +134,7 @@
public static Signature getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
if (provider == null || provider.isEmpty()) {
throw new IllegalArgumentException();
@@ -165,7 +165,7 @@
public static Signature getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
if (provider == null) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/security/Signer.java b/luni/src/main/java/java/security/Signer.java
index 1e4412a..b892090 100644
--- a/luni/src/main/java/java/security/Signer.java
+++ b/luni/src/main/java/java/security/Signer.java
@@ -83,7 +83,7 @@
*/
public final void setKeyPair(KeyPair pair) throws InvalidParameterException, KeyException {
if (pair == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pair == null");
}
if (pair.getPrivate() == null || pair.getPublic() == null) {
diff --git a/luni/src/main/java/java/security/cert/CertPathBuilder.java b/luni/src/main/java/java/security/cert/CertPathBuilder.java
index aa65fe7..42029e5 100644
--- a/luni/src/main/java/java/security/cert/CertPathBuilder.java
+++ b/luni/src/main/java/java/security/cert/CertPathBuilder.java
@@ -102,7 +102,7 @@
public static CertPathBuilder getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new CertPathBuilder((CertPathBuilderSpi) sap.spi, sap.provider, algorithm);
@@ -128,7 +128,7 @@
public static CertPathBuilder getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null || provider.isEmpty()");
}
Provider impProvider = Security.getProvider(provider);
if (impProvider == null) {
@@ -156,10 +156,10 @@
public static CertPathBuilder getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
if (provider == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new CertPathBuilder((CertPathBuilderSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/cert/CertPathValidator.java b/luni/src/main/java/java/security/cert/CertPathValidator.java
index 69b9f99..ddf78bf 100644
--- a/luni/src/main/java/java/security/cert/CertPathValidator.java
+++ b/luni/src/main/java/java/security/cert/CertPathValidator.java
@@ -101,7 +101,7 @@
public static CertPathValidator getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new CertPathValidator((CertPathValidatorSpi) sap.spi, sap.provider, algorithm);
@@ -160,7 +160,7 @@
throw new IllegalArgumentException();
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new CertPathValidator((CertPathValidatorSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/java/security/cert/CertStore.java b/luni/src/main/java/java/security/cert/CertStore.java
index 6cdaea7..2e28828 100644
--- a/luni/src/main/java/java/security/cert/CertStore.java
+++ b/luni/src/main/java/java/security/cert/CertStore.java
@@ -97,7 +97,7 @@
public static CertStore getInstance(String type, CertStoreParameters params)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Engine.SpiAndProvider sap = ENGINE.getInstance(type, params);
@@ -140,7 +140,7 @@
throws InvalidAlgorithmParameterException,
NoSuchAlgorithmException, NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null || provider.isEmpty()");
}
Provider impProvider = Security.getProvider(provider);
if (impProvider == null) {
@@ -172,10 +172,10 @@
CertStoreParameters params, Provider provider)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
if (provider == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null");
}
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Object spi = ENGINE.getInstance(type, provider, params);
diff --git a/luni/src/main/java/java/security/cert/CertificateFactory.java b/luni/src/main/java/java/security/cert/CertificateFactory.java
index 1aac1a0..83d40d3 100644
--- a/luni/src/main/java/java/security/cert/CertificateFactory.java
+++ b/luni/src/main/java/java/security/cert/CertificateFactory.java
@@ -84,7 +84,7 @@
public static final CertificateFactory getInstance(String type)
throws CertificateException {
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Engine.SpiAndProvider sap = ENGINE.getInstance(type, null);
@@ -117,7 +117,7 @@
String provider) throws CertificateException,
NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null || provider.isEmpty()");
}
Provider impProvider = Security.getProvider(provider);
if (impProvider == null) {
@@ -147,10 +147,10 @@
public static final CertificateFactory getInstance(String type,
Provider provider) throws CertificateException {
if (provider == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("provider == null");
}
if (type == null) {
- throw new NullPointerException();
+ throw new NullPointerException("type == null");
}
try {
Object spi = ENGINE.getInstance(type, provider, null);
diff --git a/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java b/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java
index de3c85d..9373b40 100644
--- a/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java
+++ b/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java
@@ -58,10 +58,10 @@
* if {@code collection is null}.
*/
public CollectionCertStoreParameters(Collection<?> collection) {
- this.collection = collection;
- if (this.collection == null) {
- throw new NullPointerException();
+ if (collection == null) {
+ throw new NullPointerException("collection == null");
}
+ this.collection = collection;
}
/**
diff --git a/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java b/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java
index 5b01f90..163c99a 100644
--- a/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java
+++ b/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java
@@ -43,11 +43,11 @@
* is {@code serverName} is {@code null}.
*/
public LDAPCertStoreParameters(String serverName, int port) {
+ if (serverName == null) {
+ throw new NullPointerException("serverName == null");
+ }
this.port = port;
this.serverName = serverName;
- if (this.serverName == null) {
- throw new NullPointerException();
- }
}
/**
@@ -71,11 +71,11 @@
* if {@code serverName} is {@code null}.
*/
public LDAPCertStoreParameters(String serverName) {
+ if (serverName == null) {
+ throw new NullPointerException("serverName == null");
+ }
this.port = DEFAULT_LDAP_PORT;
this.serverName = serverName;
- if (this.serverName == null) {
- throw new NullPointerException();
- }
}
/**
diff --git a/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java b/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java
index 589ce82..2359612 100644
--- a/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java
+++ b/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java
@@ -49,10 +49,10 @@
public PKIXCertPathBuilderResult(CertPath certPath, TrustAnchor trustAnchor,
PolicyNode policyTree, PublicKey subjectPublicKey) {
super(trustAnchor, policyTree, subjectPublicKey);
- this.certPath = certPath;
- if (this.certPath == null) {
+ if (certPath == null) {
throw new NullPointerException("certPath == null");
}
+ this.certPath = certPath;
}
/**
diff --git a/luni/src/main/java/java/security/cert/X509CRL.java b/luni/src/main/java/java/security/cert/X509CRL.java
index 4badd59..4addb0e 100644
--- a/luni/src/main/java/java/security/cert/X509CRL.java
+++ b/luni/src/main/java/java/security/cert/X509CRL.java
@@ -218,7 +218,7 @@
*/
public X509CRLEntry getRevokedCertificate(X509Certificate certificate) {
if (certificate == null) {
- throw new NullPointerException();
+ throw new NullPointerException("certificate == null");
}
return getRevokedCertificate(certificate.getSerialNumber());
}
diff --git a/luni/src/main/java/java/security/spec/ECGenParameterSpec.java b/luni/src/main/java/java/security/spec/ECGenParameterSpec.java
index fe66b1e..c22038d 100644
--- a/luni/src/main/java/java/security/spec/ECGenParameterSpec.java
+++ b/luni/src/main/java/java/security/spec/ECGenParameterSpec.java
@@ -35,7 +35,7 @@
public ECGenParameterSpec(String name) {
this.name = name;
if (this.name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
}
diff --git a/luni/src/main/java/java/sql/DataTruncation.java b/luni/src/main/java/java/sql/DataTruncation.java
index b01100e..c1bf11c 100644
--- a/luni/src/main/java/java/sql/DataTruncation.java
+++ b/luni/src/main/java/java/sql/DataTruncation.java
@@ -49,7 +49,7 @@
/**
* Creates the {@code DataTruncation} object. The reason is set to {@code
- * "Data truncation"}, the {@code ErrorCode} is set to the {@code
+ * "Data truncation"}, the error code is set to the {@code
* SQLException} default value, and the other fields are set to the values
* supplied as arguments.
*
@@ -79,7 +79,7 @@
/**
* Creates a DataTruncation. The Reason is set to "Data truncation", the
- * ErrorCode is set to the SQLException default value and other fields are
+ * error code is set to the SQLException default value and other fields are
* set to the values supplied on this method.
*
* @param index
diff --git a/luni/src/main/java/java/sql/DriverManager.java b/luni/src/main/java/java/sql/DriverManager.java
index 4cee1fe..c547585 100644
--- a/luni/src/main/java/java/sql/DriverManager.java
+++ b/luni/src/main/java/java/sql/DriverManager.java
@@ -329,7 +329,7 @@
*/
public static void registerDriver(Driver driver) throws SQLException {
if (driver == null) {
- throw new NullPointerException();
+ throw new NullPointerException("driver == null");
}
synchronized (theDrivers) {
theDrivers.add(driver);
diff --git a/luni/src/main/java/java/text/AttributedString.java b/luni/src/main/java/java/text/AttributedString.java
index 1335067..d679283 100644
--- a/luni/src/main/java/java/text/AttributedString.java
+++ b/luni/src/main/java/java/text/AttributedString.java
@@ -519,7 +519,7 @@
*/
public AttributedString(String value) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
text = value;
attributeMap = new HashMap<Attribute, List<Range>>(11);
@@ -542,7 +542,7 @@
public AttributedString(String value,
Map<? extends AttributedCharacterIterator.Attribute, ?> attributes) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
if (value.length() == 0 && !attributes.isEmpty()) {
throw new IllegalArgumentException("Cannot add attributes to empty string");
@@ -575,7 +575,7 @@
*/
public void addAttribute(AttributedCharacterIterator.Attribute attribute, Object value) {
if (attribute == null) {
- throw new NullPointerException();
+ throw new NullPointerException("attribute == null");
}
if (text.length() == 0) {
throw new IllegalArgumentException();
@@ -612,7 +612,7 @@
public void addAttribute(AttributedCharacterIterator.Attribute attribute,
Object value, int start, int end) {
if (attribute == null) {
- throw new NullPointerException();
+ throw new NullPointerException("attribute == null");
}
if (start < 0 || end > text.length() || start >= end) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/text/Collator.java b/luni/src/main/java/java/text/Collator.java
index 0fa8c71..2ddb516 100644
--- a/luni/src/main/java/java/text/Collator.java
+++ b/luni/src/main/java/java/text/Collator.java
@@ -286,7 +286,7 @@
*/
public static Collator getInstance(Locale locale) {
if (locale == null) {
- throw new NullPointerException();
+ throw new NullPointerException("locale == null");
}
return new RuleBasedCollator(new RuleBasedCollatorICU(locale));
}
diff --git a/luni/src/main/java/java/text/DateFormat.java b/luni/src/main/java/java/text/DateFormat.java
index 4055d20..b45e699 100644
--- a/luni/src/main/java/java/text/DateFormat.java
+++ b/luni/src/main/java/java/text/DateFormat.java
@@ -286,10 +286,6 @@
/**
* Returns a new instance of {@code DateFormat} with the same properties.
- *
- * @return a shallow copy of this {@code DateFormat}.
- *
- * @see java.lang.Cloneable
*/
@Override
public Object clone() {
diff --git a/luni/src/main/java/java/text/DateFormatSymbols.java b/luni/src/main/java/java/text/DateFormatSymbols.java
index 3c4768d..e2a2345 100644
--- a/luni/src/main/java/java/text/DateFormatSymbols.java
+++ b/luni/src/main/java/java/text/DateFormatSymbols.java
@@ -142,7 +142,7 @@
*/
public static final DateFormatSymbols getInstance(Locale locale) {
if (locale == null) {
- throw new NullPointerException();
+ throw new NullPointerException("locale == null");
}
return new DateFormatSymbols(locale);
}
@@ -410,7 +410,7 @@
*/
public void setLocalPatternChars(String data) {
if (data == null) {
- throw new NullPointerException();
+ throw new NullPointerException("data == null");
}
localPatternChars = data;
}
@@ -471,7 +471,7 @@
*/
public void setZoneStrings(String[][] zoneStrings) {
if (zoneStrings == null) {
- throw new NullPointerException();
+ throw new NullPointerException("zoneStrings == null");
}
for (String[] row : zoneStrings) {
if (row.length < 5) {
diff --git a/luni/src/main/java/java/text/DecimalFormat.java b/luni/src/main/java/java/text/DecimalFormat.java
index c1a48db..948bec1 100644
--- a/luni/src/main/java/java/text/DecimalFormat.java
+++ b/luni/src/main/java/java/text/DecimalFormat.java
@@ -501,16 +501,7 @@
private transient DecimalFormatSymbols symbols;
- private transient NativeDecimalFormat dform;
- private final Object finalizerGuardian = new Object() {
- @Override protected void finalize() throws Throwable {
- try {
- dform.close();
- } finally {
- super.finalize();
- }
- }
- };
+ private transient NativeDecimalFormat ndf;
private transient RoundingMode roundingMode = RoundingMode.HALF_EVEN;
@@ -562,14 +553,14 @@
private void initNative(String pattern) {
try {
- this.dform = new NativeDecimalFormat(pattern, symbols);
+ this.ndf = new NativeDecimalFormat(pattern, symbols);
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException(pattern);
}
- super.setMaximumFractionDigits(dform.getMaximumFractionDigits());
- super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits());
- super.setMinimumFractionDigits(dform.getMinimumFractionDigits());
- super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits());
+ super.setMaximumFractionDigits(ndf.getMaximumFractionDigits());
+ super.setMaximumIntegerDigits(ndf.getMaximumIntegerDigits());
+ super.setMinimumFractionDigits(ndf.getMinimumFractionDigits());
+ super.setMinimumIntegerDigits(ndf.getMinimumIntegerDigits());
}
/**
@@ -582,7 +573,7 @@
* if the pattern cannot be parsed.
*/
public void applyLocalizedPattern(String pattern) {
- dform.applyLocalizedPattern(pattern);
+ ndf.applyLocalizedPattern(pattern);
}
/**
@@ -595,20 +586,17 @@
* if the pattern cannot be parsed.
*/
public void applyPattern(String pattern) {
- dform.applyPattern(pattern);
+ ndf.applyPattern(pattern);
}
/**
* Returns a new instance of {@code DecimalFormat} with the same pattern and
- * properties as this decimal format.
- *
- * @return a shallow copy of this decimal format.
- * @see java.lang.Cloneable
+ * properties.
*/
@Override
public Object clone() {
DecimalFormat clone = (DecimalFormat) super.clone();
- clone.dform = (NativeDecimalFormat) dform.clone();
+ clone.ndf = (NativeDecimalFormat) ndf.clone();
clone.symbols = (DecimalFormatSymbols) symbols.clone();
return clone;
}
@@ -633,7 +621,7 @@
return false;
}
DecimalFormat other = (DecimalFormat) object;
- return (this.dform == null ? other.dform == null : this.dform.equals(other.dform)) &&
+ return (this.ndf == null ? other.ndf == null : this.ndf.equals(other.ndf)) &&
getDecimalFormatSymbols().equals(other.getDecimalFormatSymbols());
}
@@ -654,9 +642,9 @@
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object object) {
if (object == null) {
- throw new NullPointerException();
+ throw new NullPointerException("object == null");
}
- return dform.formatToCharacterIterator(object);
+ return ndf.formatToCharacterIterator(object);
}
private void checkBufferAndFieldPosition(StringBuffer buffer, FieldPosition position) {
@@ -686,14 +674,14 @@
setRoundingMode(RoundingMode.UNNECESSARY);
}
}
- buffer.append(dform.formatDouble(value, position));
+ buffer.append(ndf.formatDouble(value, position));
return buffer;
}
@Override
public StringBuffer format(long value, StringBuffer buffer, FieldPosition position) {
checkBufferAndFieldPosition(buffer, position);
- buffer.append(dform.formatLong(value, position));
+ buffer.append(ndf.formatLong(value, position));
return buffer;
}
@@ -703,12 +691,12 @@
if (number instanceof BigInteger) {
BigInteger bigInteger = (BigInteger) number;
char[] chars = (bigInteger.bitLength() < 64)
- ? dform.formatLong(bigInteger.longValue(), position)
- : dform.formatBigInteger(bigInteger, position);
+ ? ndf.formatLong(bigInteger.longValue(), position)
+ : ndf.formatBigInteger(bigInteger, position);
buffer.append(chars);
return buffer;
} else if (number instanceof BigDecimal) {
- buffer.append(dform.formatBigDecimal((BigDecimal) number, position));
+ buffer.append(ndf.formatBigDecimal((BigDecimal) number, position));
return buffer;
}
return super.format(number, buffer, position);
@@ -743,7 +731,7 @@
* @return the number of digits grouped together.
*/
public int getGroupingSize() {
- return dform.getGroupingSize();
+ return ndf.getGroupingSize();
}
/**
@@ -753,7 +741,7 @@
* @return the multiplier.
*/
public int getMultiplier() {
- return dform.getMultiplier();
+ return ndf.getMultiplier();
}
/**
@@ -762,7 +750,7 @@
* @return the negative prefix.
*/
public String getNegativePrefix() {
- return dform.getNegativePrefix();
+ return ndf.getNegativePrefix();
}
/**
@@ -771,7 +759,7 @@
* @return the negative suffix.
*/
public String getNegativeSuffix() {
- return dform.getNegativeSuffix();
+ return ndf.getNegativeSuffix();
}
/**
@@ -780,7 +768,7 @@
* @return the positive prefix.
*/
public String getPositivePrefix() {
- return dform.getPositivePrefix();
+ return ndf.getPositivePrefix();
}
/**
@@ -789,12 +777,12 @@
* @return the positive suffix.
*/
public String getPositiveSuffix() {
- return dform.getPositiveSuffix();
+ return ndf.getPositiveSuffix();
}
@Override
public int hashCode() {
- return dform.hashCode();
+ return getPositivePrefix().hashCode();
}
/**
@@ -805,7 +793,7 @@
* {@code false} otherwise.
*/
public boolean isDecimalSeparatorAlwaysShown() {
- return dform.isDecimalSeparatorAlwaysShown();
+ return ndf.isDecimalSeparatorAlwaysShown();
}
/**
@@ -817,7 +805,7 @@
* {@code Double}.
*/
public boolean isParseBigDecimal() {
- return dform.isParseBigDecimal();
+ return ndf.isParseBigDecimal();
}
/**
@@ -838,7 +826,7 @@
// In this implementation, NativeDecimalFormat is wrapped to
// fulfill most of the format and parse feature. And this method is
// delegated to the wrapped instance of NativeDecimalFormat.
- dform.setParseIntegerOnly(value);
+ ndf.setParseIntegerOnly(value);
}
/**
@@ -850,7 +838,7 @@
*/
@Override
public boolean isParseIntegerOnly() {
- return dform.isParseIntegerOnly();
+ return ndf.isParseIntegerOnly();
}
private static final Double NEGATIVE_ZERO_DOUBLE = new Double(-0.0);
@@ -880,7 +868,7 @@
*/
@Override
public Number parse(String string, ParsePosition position) {
- Number number = dform.parse(string, position);
+ Number number = ndf.parse(string, position);
if (number == null) {
return null;
}
@@ -918,7 +906,7 @@
if (value != null) {
// The Java object is canonical, and we copy down to native code.
this.symbols = (DecimalFormatSymbols) value.clone();
- dform.setDecimalFormatSymbols(this.symbols);
+ ndf.setDecimalFormatSymbols(this.symbols);
}
}
@@ -932,7 +920,7 @@
*/
@Override
public void setCurrency(Currency currency) {
- dform.setCurrency(Currency.getInstance(currency.getCurrencyCode()));
+ ndf.setCurrency(Currency.getInstance(currency.getCurrencyCode()));
symbols.setCurrency(currency);
}
@@ -945,7 +933,7 @@
* formatted; {@code false} otherwise.
*/
public void setDecimalSeparatorAlwaysShown(boolean value) {
- dform.setDecimalSeparatorAlwaysShown(value);
+ ndf.setDecimalSeparatorAlwaysShown(value);
}
/**
@@ -957,7 +945,7 @@
* the number of digits grouped together.
*/
public void setGroupingSize(int value) {
- dform.setGroupingSize(value);
+ ndf.setGroupingSize(value);
}
/**
@@ -969,7 +957,7 @@
*/
@Override
public void setGroupingUsed(boolean value) {
- dform.setGroupingUsed(value);
+ ndf.setGroupingUsed(value);
}
/**
@@ -979,7 +967,7 @@
*/
@Override
public boolean isGroupingUsed() {
- return dform.isGroupingUsed();
+ return ndf.isGroupingUsed();
}
/**
@@ -992,7 +980,7 @@
@Override
public void setMaximumFractionDigits(int value) {
super.setMaximumFractionDigits(value);
- dform.setMaximumFractionDigits(getMaximumFractionDigits());
+ ndf.setMaximumFractionDigits(getMaximumFractionDigits());
// Changing the maximum fraction digits needs to update ICU4C's rounding configuration.
setRoundingMode(roundingMode);
}
@@ -1007,7 +995,7 @@
@Override
public void setMaximumIntegerDigits(int value) {
super.setMaximumIntegerDigits(value);
- dform.setMaximumIntegerDigits(getMaximumIntegerDigits());
+ ndf.setMaximumIntegerDigits(getMaximumIntegerDigits());
}
/**
@@ -1020,7 +1008,7 @@
@Override
public void setMinimumFractionDigits(int value) {
super.setMinimumFractionDigits(value);
- dform.setMinimumFractionDigits(getMinimumFractionDigits());
+ ndf.setMinimumFractionDigits(getMinimumFractionDigits());
}
/**
@@ -1033,7 +1021,7 @@
@Override
public void setMinimumIntegerDigits(int value) {
super.setMinimumIntegerDigits(value);
- dform.setMinimumIntegerDigits(getMinimumIntegerDigits());
+ ndf.setMinimumIntegerDigits(getMinimumIntegerDigits());
}
/**
@@ -1044,7 +1032,7 @@
* the multiplier.
*/
public void setMultiplier(int value) {
- dform.setMultiplier(value);
+ ndf.setMultiplier(value);
}
/**
@@ -1054,7 +1042,7 @@
* the negative prefix.
*/
public void setNegativePrefix(String value) {
- dform.setNegativePrefix(value);
+ ndf.setNegativePrefix(value);
}
/**
@@ -1064,7 +1052,7 @@
* the negative suffix.
*/
public void setNegativeSuffix(String value) {
- dform.setNegativeSuffix(value);
+ ndf.setNegativeSuffix(value);
}
/**
@@ -1074,7 +1062,7 @@
* the positive prefix.
*/
public void setPositivePrefix(String value) {
- dform.setPositivePrefix(value);
+ ndf.setPositivePrefix(value);
}
/**
@@ -1084,7 +1072,7 @@
* the positive suffix.
*/
public void setPositiveSuffix(String value) {
- dform.setPositiveSuffix(value);
+ ndf.setPositiveSuffix(value);
}
/**
@@ -1096,7 +1084,7 @@
* {@code BigDecimal}; {@code false} otherwise.
*/
public void setParseBigDecimal(boolean newValue) {
- dform.setParseBigDecimal(newValue);
+ ndf.setParseBigDecimal(newValue);
}
/**
@@ -1106,7 +1094,7 @@
* @return the localized pattern.
*/
public String toLocalizedPattern() {
- return dform.toLocalizedPattern();
+ return ndf.toLocalizedPattern();
}
/**
@@ -1116,7 +1104,7 @@
* @return the non-localized pattern.
*/
public String toPattern() {
- return dform.toPattern();
+ return ndf.toPattern();
}
// the fields list to be serialized
@@ -1157,27 +1145,27 @@
*/
private void writeObject(ObjectOutputStream stream) throws IOException, ClassNotFoundException {
ObjectOutputStream.PutField fields = stream.putFields();
- fields.put("positivePrefix", dform.getPositivePrefix());
- fields.put("positiveSuffix", dform.getPositiveSuffix());
- fields.put("negativePrefix", dform.getNegativePrefix());
- fields.put("negativeSuffix", dform.getNegativeSuffix());
+ fields.put("positivePrefix", ndf.getPositivePrefix());
+ fields.put("positiveSuffix", ndf.getPositiveSuffix());
+ fields.put("negativePrefix", ndf.getNegativePrefix());
+ fields.put("negativeSuffix", ndf.getNegativeSuffix());
fields.put("posPrefixPattern", (String) null);
fields.put("posSuffixPattern", (String) null);
fields.put("negPrefixPattern", (String) null);
fields.put("negSuffixPattern", (String) null);
- fields.put("multiplier", dform.getMultiplier());
- fields.put("groupingSize", (byte) dform.getGroupingSize());
- fields.put("groupingUsed", dform.isGroupingUsed());
- fields.put("decimalSeparatorAlwaysShown", dform.isDecimalSeparatorAlwaysShown());
- fields.put("parseBigDecimal", dform.isParseBigDecimal());
+ fields.put("multiplier", ndf.getMultiplier());
+ fields.put("groupingSize", (byte) ndf.getGroupingSize());
+ fields.put("groupingUsed", ndf.isGroupingUsed());
+ fields.put("decimalSeparatorAlwaysShown", ndf.isDecimalSeparatorAlwaysShown());
+ fields.put("parseBigDecimal", ndf.isParseBigDecimal());
fields.put("roundingMode", roundingMode);
fields.put("symbols", symbols);
fields.put("useExponentialNotation", false);
fields.put("minExponentDigits", (byte) 0);
- fields.put("maximumIntegerDigits", dform.getMaximumIntegerDigits());
- fields.put("minimumIntegerDigits", dform.getMinimumIntegerDigits());
- fields.put("maximumFractionDigits", dform.getMaximumFractionDigits());
- fields.put("minimumFractionDigits", dform.getMinimumFractionDigits());
+ fields.put("maximumIntegerDigits", ndf.getMaximumIntegerDigits());
+ fields.put("minimumIntegerDigits", ndf.getMinimumIntegerDigits());
+ fields.put("maximumFractionDigits", ndf.getMaximumFractionDigits());
+ fields.put("minimumFractionDigits", ndf.getMinimumFractionDigits());
fields.put("serialVersionOnStream", 4);
stream.writeFields();
}
@@ -1198,14 +1186,14 @@
this.symbols = (DecimalFormatSymbols) fields.get("symbols", null);
initNative("");
- dform.setPositivePrefix((String) fields.get("positivePrefix", ""));
- dform.setPositiveSuffix((String) fields.get("positiveSuffix", ""));
- dform.setNegativePrefix((String) fields.get("negativePrefix", "-"));
- dform.setNegativeSuffix((String) fields.get("negativeSuffix", ""));
- dform.setMultiplier(fields.get("multiplier", 1));
- dform.setGroupingSize(fields.get("groupingSize", (byte) 3));
- dform.setGroupingUsed(fields.get("groupingUsed", true));
- dform.setDecimalSeparatorAlwaysShown(fields.get("decimalSeparatorAlwaysShown", false));
+ ndf.setPositivePrefix((String) fields.get("positivePrefix", ""));
+ ndf.setPositiveSuffix((String) fields.get("positiveSuffix", ""));
+ ndf.setNegativePrefix((String) fields.get("negativePrefix", "-"));
+ ndf.setNegativeSuffix((String) fields.get("negativeSuffix", ""));
+ ndf.setMultiplier(fields.get("multiplier", 1));
+ ndf.setGroupingSize(fields.get("groupingSize", (byte) 3));
+ ndf.setGroupingUsed(fields.get("groupingUsed", true));
+ ndf.setDecimalSeparatorAlwaysShown(fields.get("decimalSeparatorAlwaysShown", false));
setRoundingMode((RoundingMode) fields.get("roundingMode", RoundingMode.HALF_EVEN));
@@ -1218,8 +1206,8 @@
// behavior in this area is, and it's not obvious how we can second-guess ICU (or tell
// it to just do exactly what we ask). We only need to do this with maximumIntegerDigits
// because ICU doesn't seem to have its own ideas about the other options.
- dform.setMaximumIntegerDigits(maximumIntegerDigits);
- super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits());
+ ndf.setMaximumIntegerDigits(maximumIntegerDigits);
+ super.setMaximumIntegerDigits(ndf.getMaximumIntegerDigits());
setMinimumIntegerDigits(minimumIntegerDigits);
setMinimumFractionDigits(minimumFractionDigits);
@@ -1248,12 +1236,12 @@
*/
public void setRoundingMode(RoundingMode roundingMode) {
if (roundingMode == null) {
- throw new NullPointerException();
+ throw new NullPointerException("roundingMode == null");
}
this.roundingMode = roundingMode;
if (roundingMode != RoundingMode.UNNECESSARY) { // ICU4C doesn't support UNNECESSARY.
double roundingIncrement = 1.0 / Math.pow(10, Math.max(0, getMaximumFractionDigits()));
- dform.setRoundingMode(roundingMode, roundingIncrement);
+ ndf.setRoundingMode(roundingMode, roundingIncrement);
}
}
}
diff --git a/luni/src/main/java/java/text/DecimalFormatSymbols.java b/luni/src/main/java/java/text/DecimalFormatSymbols.java
index 9d2bcc1..708b291 100644
--- a/luni/src/main/java/java/text/DecimalFormatSymbols.java
+++ b/luni/src/main/java/java/text/DecimalFormatSymbols.java
@@ -127,7 +127,7 @@
*/
public static DecimalFormatSymbols getInstance(Locale locale) {
if (locale == null) {
- throw new NullPointerException();
+ throw new NullPointerException("locale == null");
}
return new DecimalFormatSymbols(locale);
}
@@ -389,7 +389,7 @@
*/
public void setCurrency(Currency currency) {
if (currency == null) {
- throw new NullPointerException();
+ throw new NullPointerException("currency == null");
}
if (currency == this.currency) {
return;
@@ -558,7 +558,7 @@
*/
public void setExponentSeparator(String value) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
this.exponentSeparator = value;
}
diff --git a/luni/src/main/java/java/text/MessageFormat.java b/luni/src/main/java/java/text/MessageFormat.java
index a98e4fd..2ab78db 100644
--- a/luni/src/main/java/java/text/MessageFormat.java
+++ b/luni/src/main/java/java/text/MessageFormat.java
@@ -506,7 +506,7 @@
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object object) {
if (object == null) {
- throw new NullPointerException();
+ throw new NullPointerException("object == null");
}
StringBuffer buffer = new StringBuffer();
diff --git a/luni/src/main/java/java/text/NumberFormat.java b/luni/src/main/java/java/text/NumberFormat.java
index 3bff6ad..c285e3d 100644
--- a/luni/src/main/java/java/text/NumberFormat.java
+++ b/luni/src/main/java/java/text/NumberFormat.java
@@ -165,11 +165,7 @@
}
/**
- * Returns a new {@code NumberFormat} with the same properties as this
- * {@code NumberFormat}.
- *
- * @return a shallow copy of this {@code NumberFormat}.
- * @see java.lang.Cloneable
+ * Returns a new {@code NumberFormat} with the same properties.
*/
@Override
public Object clone() {
@@ -570,7 +566,7 @@
@Override
public final Object parseObject(String string, ParsePosition position) {
if (position == null) {
- throw new NullPointerException("position is null");
+ throw new NullPointerException("position == null");
}
try {
return parse(string, position);
diff --git a/luni/src/main/java/java/text/RuleBasedCollator.java b/luni/src/main/java/java/text/RuleBasedCollator.java
index 4fd8650..cda06db 100644
--- a/luni/src/main/java/java/text/RuleBasedCollator.java
+++ b/luni/src/main/java/java/text/RuleBasedCollator.java
@@ -284,7 +284,7 @@
*/
public RuleBasedCollator(String rules) throws ParseException {
if (rules == null) {
- throw new NullPointerException();
+ throw new NullPointerException("rules == null");
}
if (rules.isEmpty()) {
throw new ParseException("empty rules", 0);
@@ -314,7 +314,7 @@
*/
public CollationElementIterator getCollationElementIterator(CharacterIterator source) {
if (source == null) {
- throw new NullPointerException();
+ throw new NullPointerException("source == null");
}
return new CollationElementIterator(icuColl.getCollationElementIterator(source));
}
@@ -328,7 +328,7 @@
*/
public CollationElementIterator getCollationElementIterator(String source) {
if (source == null) {
- throw new NullPointerException();
+ throw new NullPointerException("source == null");
}
return new CollationElementIterator(icuColl.getCollationElementIterator(source));
}
@@ -385,8 +385,10 @@
*/
@Override
public int compare(String source, String target) {
- if (source == null || target == null) {
- throw new NullPointerException();
+ if (source == null) {
+ throw new NullPointerException("source == null");
+ } else if (target == null) {
+ throw new NullPointerException("target == null");
}
return icuColl.compare(source, target);
}
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index 1f9b694..f682c0b 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -411,9 +411,6 @@
/**
* Returns a new {@code SimpleDateFormat} with the same pattern and
* properties as this simple date format.
- *
- * @return a shallow copy of this simple date format.
- * @see java.lang.Cloneable
*/
@Override
public Object clone() {
@@ -471,7 +468,7 @@
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object object) {
if (object == null) {
- throw new NullPointerException();
+ throw new NullPointerException("object == null");
}
if (object instanceof Date) {
return formatToCharacterIteratorImpl((Date) object);
@@ -1070,24 +1067,42 @@
}
private Number parseNumber(int max, String string, ParsePosition position) {
- int digit, length = string.length(), result = 0;
+ int length = string.length();
int index = position.getIndex();
if (max > 0 && max < length - index) {
length = index + max;
}
- while (index < length
- && (string.charAt(index) == ' ' || string.charAt(index) == '\t')) {
- index++;
+ while (index < length && (string.charAt(index) == ' ' || string.charAt(index) == '\t')) {
+ ++index;
}
if (max == 0) {
position.setIndex(index);
- return numberFormat.parse(string, position);
+ Number n = numberFormat.parse(string, position);
+ // In RTL locales, NumberFormat might have parsed "2012-" in an ISO date as the
+ // negative number -2012.
+ // Ideally, we wouldn't have this broken API that exposes a NumberFormat and expects
+ // us to use it. The next best thing would be a way to ask the NumberFormat to parse
+ // positive numbers only, but icu4c supports negative (BCE) years. The best we can do
+ // is try to recognize when icu4c has done this, and undo it.
+ if (n != null && n.longValue() < 0) {
+ if (numberFormat instanceof DecimalFormat) {
+ DecimalFormat df = (DecimalFormat) numberFormat;
+ char lastChar = string.charAt(position.getIndex() - 1);
+ char minusSign = df.getDecimalFormatSymbols().getMinusSign();
+ if (lastChar == minusSign) {
+ n = Long.valueOf(-n.longValue()); // Make the value positive.
+ position.setIndex(position.getIndex() - 1); // Spit out the negative sign.
+ }
+ }
+ }
+ return n;
}
- while (index < length
- && (digit = Character.digit(string.charAt(index), 10)) != -1) {
- index++;
+ int result = 0;
+ int digit;
+ while (index < length && (digit = Character.digit(string.charAt(index), 10)) != -1) {
result = result * 10 + digit;
+ ++index;
}
if (index == position.getIndex()) {
position.setErrorIndex(index);
@@ -1174,14 +1189,18 @@
}
int raw = zone.getRawOffset();
if (j == TimeZones.LONG_NAME_DST || j == TimeZones.SHORT_NAME_DST) {
- /*
- * TODO, http://b/4723412
- * We can't use TimeZone#getDSTSavings here because that
- * will return 0 if the zone no longer uses DST. We
- * should change this to use TimeZone.getOffset(long),
- * which requires the complete date to be parsed first.
- */
- raw += 3600000;
+ // Not all time zones use a one-hour difference, so we need to query
+ // the TimeZone. (Australia/Lord_Howe is the usual example of this.)
+ int dstSavings = zone.getDSTSavings();
+ // One problem with TimeZone.getDSTSavings is that it will return 0 if the
+ // time zone has stopped using DST, even if we're parsing a date from
+ // the past. In that case, assume the default.
+ if (dstSavings == 0) {
+ // TODO: we should change this to use TimeZone.getOffset(long),
+ // but that requires the complete date to be parsed first.
+ dstSavings = 3600000;
+ }
+ raw += dstSavings;
}
calendar.setTimeZone(new SimpleTimeZone(raw, ""));
return offset + element[j].length();
diff --git a/luni/src/main/java/java/util/AbstractQueue.java b/luni/src/main/java/java/util/AbstractQueue.java
index d368ac9..47f81fd 100644
--- a/luni/src/main/java/java/util/AbstractQueue.java
+++ b/luni/src/main/java/java/util/AbstractQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
@@ -150,9 +150,9 @@
*/
public boolean addAll(Collection<? extends E> c) {
if (c == null)
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
if (c == this)
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("c == this");
boolean modified = false;
for (E e : c)
if (add(e))
diff --git a/luni/src/main/java/java/util/ArrayDeque.java b/luni/src/main/java/java/util/ArrayDeque.java
index fafcdb4..5ee3f81 100644
--- a/luni/src/main/java/java/util/ArrayDeque.java
+++ b/luni/src/main/java/java/util/ArrayDeque.java
@@ -1,6 +1,6 @@
/*
* Written by Josh Bloch of Google Inc. and released to the public domain,
- * as explained at http://creativecommons.org/licenses/publicdomain.
+ * as explained at http://creativecommons.org/publicdomain/zero/1.0/.
*/
package java.util;
@@ -9,8 +9,6 @@
// removed link to collections framework docs
// END android-note
-import java.io.*;
-
/**
* Resizable-array implementation of the {@link Deque} interface. Array
* deques have no capacity restrictions; they grow as necessary to support
@@ -53,7 +51,7 @@
* @param <E> the type of elements held in this collection
*/
public class ArrayDeque<E> extends AbstractCollection<E>
- implements Deque<E>, Cloneable, Serializable
+ implements Deque<E>, Cloneable, java.io.Serializable
{
/**
* The array in which the elements of the deque are stored.
@@ -65,7 +63,7 @@
* other. We also guarantee that all array cells not holding
* deque elements are always null.
*/
- private transient E[] elements;
+ private transient Object[] elements;
/**
* The index of the element at the head of the deque (which is the
@@ -109,7 +107,7 @@
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
- elements = (E[]) new Object[initialCapacity];
+ elements = new Object[initialCapacity];
}
/**
@@ -127,7 +125,7 @@
Object[] a = new Object[newCapacity];
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
- elements = (E[])a;
+ elements = a;
head = 0;
tail = n;
}
@@ -155,7 +153,7 @@
* sufficient to hold 16 elements.
*/
public ArrayDeque() {
- elements = (E[]) new Object[16];
+ elements = new Object[16];
}
/**
@@ -195,7 +193,7 @@
*/
public void addFirst(E e) {
if (e == null)
- throw new NullPointerException();
+ throw new NullPointerException("e == null");
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
@@ -211,7 +209,7 @@
*/
public void addLast(E e) {
if (e == null)
- throw new NullPointerException();
+ throw new NullPointerException("e == null");
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
@@ -263,7 +261,8 @@
public E pollFirst() {
int h = head;
- E result = elements[h]; // Element is null if deque empty
+ @SuppressWarnings("unchecked") E result = (E) elements[h];
+ // Element is null if deque empty
if (result == null)
return null;
elements[h] = null; // Must null out slot
@@ -273,7 +272,7 @@
public E pollLast() {
int t = (tail - 1) & (elements.length - 1);
- E result = elements[t];
+ @SuppressWarnings("unchecked") E result = (E) elements[t];
if (result == null)
return null;
elements[t] = null;
@@ -285,28 +284,33 @@
* @throws NoSuchElementException {@inheritDoc}
*/
public E getFirst() {
- E x = elements[head];
- if (x == null)
+ @SuppressWarnings("unchecked") E result = (E) elements[head];
+ if (result == null)
throw new NoSuchElementException();
- return x;
+ return result;
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
public E getLast() {
- E x = elements[(tail - 1) & (elements.length - 1)];
- if (x == null)
+ @SuppressWarnings("unchecked")
+ E result = (E) elements[(tail - 1) & (elements.length - 1)];
+ if (result == null)
throw new NoSuchElementException();
- return x;
+ return result;
}
public E peekFirst() {
- return elements[head]; // elements[head] is null if deque empty
+ @SuppressWarnings("unchecked") E result = (E) elements[head];
+ // elements[head] is null if deque empty
+ return result;
}
public E peekLast() {
- return elements[(tail - 1) & (elements.length - 1)];
+ @SuppressWarnings("unchecked")
+ E result = (E) elements[(tail - 1) & (elements.length - 1)];
+ return result;
}
/**
@@ -326,7 +330,7 @@
return false;
int mask = elements.length - 1;
int i = head;
- E x;
+ Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
@@ -354,7 +358,7 @@
return false;
int mask = elements.length - 1;
int i = (tail - 1) & mask;
- E x;
+ Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
@@ -499,7 +503,7 @@
*/
private boolean delete(int i) {
checkInvariants();
- final E[] elements = this.elements;
+ final Object[] elements = this.elements;
final int mask = elements.length - 1;
final int h = head;
final int t = tail;
@@ -597,7 +601,7 @@
public E next() {
if (cursor == fence)
throw new NoSuchElementException();
- E result = elements[cursor];
+ @SuppressWarnings("unchecked") E result = (E) elements[cursor];
// This check doesn't catch all possible comodifications,
// but does catch the ones that corrupt traversal
if (tail != fence || result == null)
@@ -636,7 +640,7 @@
if (cursor == fence)
throw new NoSuchElementException();
cursor = (cursor - 1) & (elements.length - 1);
- E result = elements[cursor];
+ @SuppressWarnings("unchecked") E result = (E) elements[cursor];
if (head != fence || result == null)
throw new ConcurrentModificationException();
lastRet = cursor;
@@ -667,7 +671,7 @@
return false;
int mask = elements.length - 1;
int i = head;
- E x;
+ Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x))
return true;
@@ -750,8 +754,7 @@
* The following code can be used to dump the deque into a newly
* allocated array of <tt>String</tt>:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
@@ -765,6 +768,7 @@
* this deque
* @throws NullPointerException if the specified array is null
*/
+ @SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
@@ -785,6 +789,7 @@
*/
public ArrayDeque<E> clone() {
try {
+ @SuppressWarnings("unchecked")
ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
result.elements = Arrays.copyOf(elements, elements.length);
return result;
@@ -806,7 +811,8 @@
* followed by all of its elements (each an object reference) in
* first-to-last order.
*/
- private void writeObject(ObjectOutputStream s) throws IOException {
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
s.defaultWriteObject();
// Write out size
@@ -821,8 +827,8 @@
/**
* Deserialize this deque.
*/
- private void readObject(ObjectInputStream s)
- throws IOException, ClassNotFoundException {
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Read in size and allocate array
@@ -833,6 +839,6 @@
// Read in all elements in the proper order.
for (int i = 0; i < size; i++)
- elements[i] = (E)s.readObject();
+ elements[i] = s.readObject();
}
}
diff --git a/luni/src/main/java/java/util/Arrays.java b/luni/src/main/java/java/util/Arrays.java
index 9d0f4a4..4a149b7 100644
--- a/luni/src/main/java/java/util/Arrays.java
+++ b/luni/src/main/java/java/util/Arrays.java
@@ -35,7 +35,7 @@
ArrayList(E[] storage) {
if (storage == null) {
- throw new NullPointerException();
+ throw new NullPointerException("storage == null");
}
a = storage;
}
@@ -2459,7 +2459,7 @@
*/
public static boolean[] copyOf(boolean[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2478,7 +2478,7 @@
*/
public static byte[] copyOf(byte[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2497,7 +2497,7 @@
*/
public static char[] copyOf(char[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2516,7 +2516,7 @@
*/
public static double[] copyOf(double[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2535,7 +2535,7 @@
*/
public static float[] copyOf(float[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2554,7 +2554,7 @@
*/
public static int[] copyOf(int[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2573,7 +2573,7 @@
*/
public static long[] copyOf(long[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2592,7 +2592,7 @@
*/
public static short[] copyOf(short[] original, int newLength) {
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2611,10 +2611,10 @@
*/
public static <T> T[] copyOf(T[] original, int newLength) {
if (original == null) {
- throw new NullPointerException();
+ throw new NullPointerException("original == null");
}
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
@@ -2636,7 +2636,7 @@
public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
// We use the null pointer check in copyOfRange for exception priority compatibility.
if (newLength < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength, newType);
}
diff --git a/luni/src/main/java/java/util/BitSet.java b/luni/src/main/java/java/util/BitSet.java
index a4ee4c1..9dfe35e 100644
--- a/luni/src/main/java/java/util/BitSet.java
+++ b/luni/src/main/java/java/util/BitSet.java
@@ -85,7 +85,7 @@
*/
public BitSet(int bitCount) {
if (bitCount < 0) {
- throw new NegativeArraySizeException();
+ throw new NegativeArraySizeException(Integer.toString(bitCount));
}
this.bits = arrayForBits(bitCount);
this.longCount = 0;
diff --git a/luni/src/main/java/java/util/Calendar.java b/luni/src/main/java/java/util/Calendar.java
index bef6e26..81d01fb 100644
--- a/luni/src/main/java/java/util/Calendar.java
+++ b/luni/src/main/java/java/util/Calendar.java
@@ -877,8 +877,7 @@
return getTimeInMillis() == cal.getTimeInMillis()
&& isLenient() == cal.isLenient()
&& getFirstDayOfWeek() == cal.getFirstDayOfWeek()
- && getMinimalDaysInFirstWeek() == cal
- .getMinimalDaysInFirstWeek()
+ && getMinimalDaysInFirstWeek() == cal.getMinimalDaysInFirstWeek()
&& getTimeZone().equals(cal.getTimeZone());
}
@@ -903,11 +902,8 @@
}
/**
- * Gets the maximum value of the specified field for the current date.
- *
- * @param field
- * the field.
- * @return the maximum value of the specified field.
+ * Returns the maximum value of the specified field for the current date.
+ * For example, the maximum number of days in the current month.
*/
public int getActualMaximum(int field) {
int value, next;
@@ -1393,7 +1389,7 @@
*/
public int compareTo(Calendar anotherCalendar) {
if (anotherCalendar == null) {
- throw new NullPointerException();
+ throw new NullPointerException("anotherCalendar == null");
}
long timeInMillis = getTimeInMillis();
long anotherTimeInMillis = anotherCalendar.getTimeInMillis();
diff --git a/luni/src/main/java/java/util/Collections.java b/luni/src/main/java/java/util/Collections.java
index b6729b4..d49ca85 100644
--- a/luni/src/main/java/java/util/Collections.java
+++ b/luni/src/main/java/java/util/Collections.java
@@ -1412,7 +1412,7 @@
@SuppressWarnings("unchecked")
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T object) {
if (list == null) {
- throw new NullPointerException();
+ throw new NullPointerException("list == null");
}
if (list.isEmpty()) {
return -1;
@@ -1916,7 +1916,7 @@
@SuppressWarnings("unchecked")
public static void swap(List<?> list, int index1, int index2) {
if (list == null) {
- throw new NullPointerException();
+ throw new NullPointerException("list == null");
}
final int size = list.size();
if (index1 < 0 || index1 >= size || index2 < 0 || index2 >= size) {
@@ -2174,7 +2174,7 @@
public static <T> Collection<T> synchronizedCollection(
Collection<T> collection) {
if (collection == null) {
- throw new NullPointerException();
+ throw new NullPointerException("collection == null");
}
return new SynchronizedCollection<T>(collection);
}
@@ -2189,7 +2189,7 @@
*/
public static <T> List<T> synchronizedList(List<T> list) {
if (list == null) {
- throw new NullPointerException();
+ throw new NullPointerException("list == null");
}
if (list instanceof RandomAccess) {
return new SynchronizedRandomAccessList<T>(list);
@@ -2207,7 +2207,7 @@
*/
public static <K, V> Map<K, V> synchronizedMap(Map<K, V> map) {
if (map == null) {
- throw new NullPointerException();
+ throw new NullPointerException("map == null");
}
return new SynchronizedMap<K, V>(map);
}
@@ -2222,7 +2222,7 @@
*/
public static <E> Set<E> synchronizedSet(Set<E> set) {
if (set == null) {
- throw new NullPointerException();
+ throw new NullPointerException("set == null");
}
return new SynchronizedSet<E>(set);
}
@@ -2238,7 +2238,7 @@
public static <K, V> SortedMap<K, V> synchronizedSortedMap(
SortedMap<K, V> map) {
if (map == null) {
- throw new NullPointerException();
+ throw new NullPointerException("map == null");
}
return new SynchronizedSortedMap<K, V>(map);
}
@@ -2253,7 +2253,7 @@
*/
public static <E> SortedSet<E> synchronizedSortedSet(SortedSet<E> set) {
if (set == null) {
- throw new NullPointerException();
+ throw new NullPointerException("set == null");
}
return new SynchronizedSortedSet<E>(set);
}
@@ -2271,7 +2271,7 @@
public static <E> Collection<E> unmodifiableCollection(
Collection<? extends E> collection) {
if (collection == null) {
- throw new NullPointerException();
+ throw new NullPointerException("collection == null");
}
return new UnmodifiableCollection<E>((Collection<E>) collection);
}
@@ -2288,7 +2288,7 @@
@SuppressWarnings("unchecked")
public static <E> List<E> unmodifiableList(List<? extends E> list) {
if (list == null) {
- throw new NullPointerException();
+ throw new NullPointerException("list == null");
}
if (list instanceof RandomAccess) {
return new UnmodifiableRandomAccessList<E>((List<E>) list);
@@ -2309,7 +2309,7 @@
public static <K, V> Map<K, V> unmodifiableMap(
Map<? extends K, ? extends V> map) {
if (map == null) {
- throw new NullPointerException();
+ throw new NullPointerException("map == null");
}
return new UnmodifiableMap<K, V>((Map<K, V>) map);
}
@@ -2326,7 +2326,7 @@
@SuppressWarnings("unchecked")
public static <E> Set<E> unmodifiableSet(Set<? extends E> set) {
if (set == null) {
- throw new NullPointerException();
+ throw new NullPointerException("set == null");
}
return new UnmodifiableSet<E>((Set<E>) set);
}
@@ -2344,7 +2344,7 @@
public static <K, V> SortedMap<K, V> unmodifiableSortedMap(
SortedMap<K, ? extends V> map) {
if (map == null) {
- throw new NullPointerException();
+ throw new NullPointerException("map == null");
}
return new UnmodifiableSortedMap<K, V>((SortedMap<K, V>) map);
}
@@ -2360,7 +2360,7 @@
*/
public static <E> SortedSet<E> unmodifiableSortedSet(SortedSet<E> set) {
if (set == null) {
- throw new NullPointerException();
+ throw new NullPointerException("set == null");
}
return new UnmodifiableSortedSet<E>(set);
}
@@ -2381,7 +2381,7 @@
*/
public static int frequency(Collection<?> c, Object o) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
if (c.isEmpty()) {
return 0;
@@ -2834,8 +2834,10 @@
Class<E> type;
public CheckedCollection(Collection<E> c, Class<E> type) {
- if (c == null || type == null) {
- throw new NullPointerException();
+ if (c == null) {
+ throw new NullPointerException("c == null");
+ } else if (type == null) {
+ throw new NullPointerException("type == null");
}
this.c = c;
this.type = type;
@@ -3079,8 +3081,12 @@
Class<V> valueType;
private CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType) {
- if (m == null || keyType == null || valueType == null) {
- throw new NullPointerException();
+ if (m == null) {
+ throw new NullPointerException("m == null");
+ } else if (keyType == null) {
+ throw new NullPointerException("keyType == null");
+ } else if (valueType == null) {
+ throw new NullPointerException("valueType == null");
}
this.m = m;
this.keyType = keyType;
@@ -3172,7 +3178,7 @@
public CheckedEntry(Map.Entry<K, V> e, Class<V> valueType) {
if (e == null) {
- throw new NullPointerException();
+ throw new NullPointerException("e == null");
}
this.e = e;
this.valueType = valueType;
diff --git a/luni/src/main/java/java/util/Deque.java b/luni/src/main/java/java/util/Deque.java
index cb6bd90..f74a6b4 100644
--- a/luni/src/main/java/java/util/Deque.java
+++ b/luni/src/main/java/java/util/Deque.java
@@ -1,14 +1,13 @@
/*
* Written by Doug Lea and Josh Bloch with assistance from members of
* JCP JSR-166 Expert Group and released to the public domain, as explained
- * at http://creativecommons.org/licenses/publicdomain
+ * at http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
// BEGIN android-note
// removed link to collections framework docs
-// changed {@link #offer(Object)} to {@link #offer} to satisfy DroidDoc
// END android-note
/**
@@ -356,7 +355,7 @@
* <tt>true</tt> upon success and throwing an
* <tt>IllegalStateException</tt> if no space is currently available.
* When using a capacity-restricted deque, it is generally preferable to
- * use {@link #offer offer}.
+ * use {@link #offer(Object) offer}.
*
* <p>This method is equivalent to {@link #addLast}.
*
diff --git a/luni/src/main/java/java/util/DualPivotQuicksort.java b/luni/src/main/java/java/util/DualPivotQuicksort.java
index 97797d1..5d2f77f 100644
--- a/luni/src/main/java/java/util/DualPivotQuicksort.java
+++ b/luni/src/main/java/java/util/DualPivotQuicksort.java
@@ -1565,7 +1565,7 @@
for (int k = left; k <= n; k++) {
float ak = a[k];
- if (ak == 0.0f && NEGATIVE_ZERO == Float.floatToIntBits(ak)) {
+ if (ak == 0.0f && NEGATIVE_ZERO == Float.floatToRawIntBits(ak)) {
a[k] = 0.0f;
numNegativeZeros++;
} else if (ak != ak) { // i.e., ak is NaN
@@ -1938,7 +1938,7 @@
for (int k = left; k <= n; k++) {
double ak = a[k];
- if (ak == 0.0d && NEGATIVE_ZERO == Double.doubleToLongBits(ak)) {
+ if (ak == 0.0d && NEGATIVE_ZERO == Double.doubleToRawLongBits(ak)) {
a[k] = 0.0d;
numNegativeZeros++;
} else if (ak != ak) { // i.e., ak is NaN
diff --git a/luni/src/main/java/java/util/DuplicateFormatFlagsException.java b/luni/src/main/java/java/util/DuplicateFormatFlagsException.java
index d04db8e..2a2bc2e 100644
--- a/luni/src/main/java/java/util/DuplicateFormatFlagsException.java
+++ b/luni/src/main/java/java/util/DuplicateFormatFlagsException.java
@@ -37,7 +37,7 @@
*/
public DuplicateFormatFlagsException(String f) {
if (f == null) {
- throw new NullPointerException();
+ throw new NullPointerException("f == null");
}
flags = f;
}
diff --git a/luni/src/main/java/java/util/EnumMap.java b/luni/src/main/java/java/util/EnumMap.java
index d3d42b4..a721ee3 100644
--- a/luni/src/main/java/java/util/EnumMap.java
+++ b/luni/src/main/java/java/util/EnumMap.java
@@ -773,7 +773,7 @@
@SuppressWarnings("unchecked")
private V putImpl(K key, V value) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
keyType.cast(key); // Called to throw ClassCastException.
int keyOrdinal = key.ordinal();
diff --git a/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java b/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java
index 5792877..5c36788 100644
--- a/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java
+++ b/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java
@@ -44,7 +44,7 @@
*/
public FormatFlagsConversionMismatchException(String f, char c) {
if (f == null) {
- throw new NullPointerException();
+ throw new NullPointerException("f == null");
}
this.f = f;
this.c = c;
diff --git a/luni/src/main/java/java/util/Formatter.java b/luni/src/main/java/java/util/Formatter.java
index e9a2f4a..021da08 100644
--- a/luni/src/main/java/java/util/Formatter.java
+++ b/luni/src/main/java/java/util/Formatter.java
@@ -884,7 +884,7 @@
*/
public Formatter(PrintStream ps) {
if (ps == null) {
- throw new NullPointerException();
+ throw new NullPointerException("ps == null");
}
out = ps;
locale = Locale.getDefault();
diff --git a/luni/src/main/java/java/util/GregorianCalendar.java b/luni/src/main/java/java/util/GregorianCalendar.java
index c0fd521..9ff9ccc 100644
--- a/luni/src/main/java/java/util/GregorianCalendar.java
+++ b/luni/src/main/java/java/util/GregorianCalendar.java
@@ -219,14 +219,6 @@
private static int[] leastMaximums = new int[] { 1, 292269054, 11, 50, 3,
28, 355, 7, 3, 1, 11, 23, 59, 59, 999, 50400000, 1200000 };
- private boolean isCached;
-
- private int[] cachedFields = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- private long nextMidnightMillis = 0L;
-
- private long lastMidnightMillis = 0L;
-
private int currentYearSkew = 10;
private int lastYearSkew = 0;
@@ -365,8 +357,6 @@
throw new IllegalArgumentException();
}
- isCached = false;
-
if (field == ERA) {
complete();
if (fields[ERA] == AD) {
@@ -468,19 +458,8 @@
complete();
}
- /**
- * Creates new instance of {@code GregorianCalendar} with the same properties.
- *
- * @return a shallow copy of this {@code GregorianCalendar}.
- */
- @Override
- public Object clone() {
- GregorianCalendar thisClone = (GregorianCalendar) super.clone();
- thisClone.cachedFields = cachedFields.clone();
- return thisClone;
- }
-
- private final void fullFieldsCalc(long timeVal, int millis, int zoneOffset) {
+ private void fullFieldsCalc(long timeVal, int zoneOffset) {
+ int millis = (int) (time % 86400000);
long days = timeVal / 86400000;
if (millis < 0) {
@@ -583,31 +562,6 @@
}
}
- private final void cachedFieldsCheckAndGet(long timeVal,
- long newTimeMillis, long newTimeMillisAdjusted, int millis,
- int zoneOffset) {
- int dstOffset = fields[DST_OFFSET];
- if (!isCached
- || newTimeMillis >= nextMidnightMillis
- || newTimeMillis <= lastMidnightMillis
- || cachedFields[4] != zoneOffset
- || (dstOffset == 0 && (newTimeMillisAdjusted >= nextMidnightMillis))
- || (dstOffset != 0 && (newTimeMillisAdjusted <= lastMidnightMillis))) {
- fullFieldsCalc(timeVal, millis, zoneOffset);
- isCached = false;
- } else {
- fields[YEAR] = cachedFields[0];
- fields[MONTH] = cachedFields[1];
- fields[DATE] = cachedFields[2];
- fields[DAY_OF_WEEK] = cachedFields[3];
- fields[ERA] = cachedFields[5];
- fields[WEEK_OF_YEAR] = cachedFields[6];
- fields[WEEK_OF_MONTH] = cachedFields[7];
- fields[DAY_OF_YEAR] = cachedFields[8];
- fields[DAY_OF_WEEK_IN_MONTH] = cachedFields[9];
- }
- }
-
@Override
protected void computeFields() {
TimeZone timeZone = getTimeZone();
@@ -616,99 +570,11 @@
fields[DST_OFFSET] = dstOffset;
fields[ZONE_OFFSET] = zoneOffset;
- int millis = (int) (time % 86400000);
- int savedMillis = millis;
- // compute without a change in daylight saving time
- int offset = zoneOffset + dstOffset;
- long newTime = time + offset;
-
- if (time > 0L && newTime < 0L && offset > 0) {
- newTime = 0x7fffffffffffffffL;
- } else if (time < 0L && newTime > 0L && offset < 0) {
- newTime = 0x8000000000000000L;
- }
-
- // FIXME: I don't think this caching ever really gets used, because it requires that the
- // time zone doesn't use daylight savings (ever). So unless you're somewhere like Taiwan...
- if (isCached) {
- if (millis < 0) {
- millis += 86400000;
- }
-
- // Cannot add ZONE_OFFSET to time as it might overflow
- millis += zoneOffset;
- millis += dstOffset;
-
- if (millis < 0) {
- millis += 86400000;
- } else if (millis >= 86400000) {
- millis -= 86400000;
- }
-
- fields[MILLISECOND] = (millis % 1000);
- millis /= 1000;
- fields[SECOND] = (millis % 60);
- millis /= 60;
- fields[MINUTE] = (millis % 60);
- millis /= 60;
- fields[HOUR_OF_DAY] = (millis % 24);
- millis /= 24;
- fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0;
- fields[HOUR] = fields[HOUR_OF_DAY] % 12;
-
- // FIXME: this has to be wrong; useDaylightTime doesn't mean what they think it means!
- long newTimeAdjusted = newTime;
- if (timeZone.useDaylightTime()) {
- int dstSavings = timeZone.getDSTSavings();
- newTimeAdjusted += (dstOffset == 0) ? dstSavings : -dstSavings;
- }
-
- if (newTime > 0L && newTimeAdjusted < 0L && dstOffset == 0) {
- newTimeAdjusted = 0x7fffffffffffffffL;
- } else if (newTime < 0L && newTimeAdjusted > 0L && dstOffset != 0) {
- newTimeAdjusted = 0x8000000000000000L;
- }
-
- cachedFieldsCheckAndGet(time, newTime, newTimeAdjusted,
- savedMillis, zoneOffset);
- } else {
- fullFieldsCalc(time, savedMillis, zoneOffset);
- }
+ fullFieldsCalc(time, zoneOffset);
for (int i = 0; i < FIELD_COUNT; i++) {
isSet[i] = true;
}
-
- // Caching
- if (!isCached
- && newTime != 0x7fffffffffffffffL
- && newTime != 0x8000000000000000L
- && (!timeZone.useDaylightTime() || timeZone instanceof SimpleTimeZone)) {
- int cacheMillis = 0;
-
- cachedFields[0] = fields[YEAR];
- cachedFields[1] = fields[MONTH];
- cachedFields[2] = fields[DATE];
- cachedFields[3] = fields[DAY_OF_WEEK];
- cachedFields[4] = zoneOffset;
- cachedFields[5] = fields[ERA];
- cachedFields[6] = fields[WEEK_OF_YEAR];
- cachedFields[7] = fields[WEEK_OF_MONTH];
- cachedFields[8] = fields[DAY_OF_YEAR];
- cachedFields[9] = fields[DAY_OF_WEEK_IN_MONTH];
-
- cacheMillis += (23 - fields[HOUR_OF_DAY]) * 60 * 60 * 1000;
- cacheMillis += (59 - fields[MINUTE]) * 60 * 1000;
- cacheMillis += (59 - fields[SECOND]) * 1000;
- nextMidnightMillis = newTime + cacheMillis;
-
- cacheMillis = fields[HOUR_OF_DAY] * 60 * 60 * 1000;
- cacheMillis += fields[MINUTE] * 60 * 1000;
- cacheMillis += fields[SECOND] * 1000;
- lastMidnightMillis = newTime - cacheMillis;
-
- isCached = true;
- }
}
@Override
@@ -939,19 +805,17 @@
return (int) days + 1;
}
- private long daysFromBaseYear(int iyear) {
- long year = iyear;
-
+ private long daysFromBaseYear(long year) {
if (year >= 1970) {
long days = (year - 1970) * 365 + ((year - 1969) / 4);
if (year > changeYear) {
days -= ((year - 1901) / 100) - ((year - 1601) / 400);
} else {
- if(year == changeYear){
+ if (year == changeYear) {
days += currentYearSkew;
- }else if(year == changeYear -1){
+ } else if (year == changeYear - 1) {
days += lastYearSkew;
- }else{
+ } else {
days += julianSkew;
}
}
@@ -995,21 +859,10 @@
}
/**
- * Compares the specified {@code Object} to this {@code GregorianCalendar} and returns whether
- * they are equal. To be equal, the {@code Object} must be an instance of {@code GregorianCalendar} and
- * have the same properties.
- *
- * @param object
- * the {@code Object} to compare with this {@code GregorianCalendar}.
- * @return {@code true} if {@code object} is equal to this
- * {@code GregorianCalendar}, {@code false} otherwise.
- * @throws IllegalArgumentException
- * if the time is not set and the time cannot be computed
- * from the current field values.
- * @see #hashCode
+ * Returns true if {@code object} is a GregorianCalendar with the same
+ * properties.
*/
- @Override
- public boolean equals(Object object) {
+ @Override public boolean equals(Object object) {
if (!(object instanceof GregorianCalendar)) {
return false;
}
@@ -1020,28 +873,12 @@
&& gregorianCutover == ((GregorianCalendar) object).gregorianCutover;
}
- /**
- * Gets the maximum value of the specified field for the current date. For
- * example, the maximum number of days in the current month.
- *
- * @param field
- * the field.
- * @return the maximum value of the specified field.
- */
- @Override
- public int getActualMaximum(int field) {
+ @Override public int getActualMaximum(int field) {
int value;
if ((value = maximums[field]) == leastMaximums[field]) {
return value;
}
- switch (field) {
- case WEEK_OF_YEAR:
- case WEEK_OF_MONTH:
- isCached = false;
- break;
- }
-
complete();
long orgTime = time;
int result = 0;
@@ -1216,32 +1053,16 @@
month++;
}
int dayOfWeek = mod7(dayCount - 3) + 1;
- int offset = timeZone.getOffset(AD, year, month, date, dayOfWeek,
- millis);
- return offset;
+ return timeZone.getOffset(AD, year, month, date, dayOfWeek, millis);
}
- /**
- * Returns an integer hash code for the receiver. Objects which are equal
- * return the same value for this method.
- *
- * @return the receiver's hash.
- *
- * @see #equals
- */
- @Override
- public int hashCode() {
+ @Override public int hashCode() {
return super.hashCode()
+ ((int) (gregorianCutover >>> 32) ^ (int) gregorianCutover);
}
/**
- * Returns whether the specified year is a leap year.
- *
- * @param year
- * the year.
- * @return {@code true} if the specified year is a leap year, {@code false}
- * otherwise.
+ * Returns true if {@code year} is a leap year.
*/
public boolean isLeapYear(int year) {
if (year > changeYear) {
@@ -1294,8 +1115,6 @@
throw new IllegalArgumentException();
}
- isCached = false;
-
complete();
int days, day, mod, maxWeeks, newWeek;
int max = -1;
@@ -1410,13 +1229,10 @@
/**
* Sets the gregorian change date of this calendar.
- *
- * @param date
- * a {@code Date} which represents the gregorian change date.
*/
public void setGregorianChange(Date date) {
gregorianCutover = date.getTime();
- GregorianCalendar cal = new GregorianCalendar(TimeZone.GMT);
+ GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
cal.setTime(date);
changeYear = cal.get(YEAR);
if (cal.get(ERA) == BC) {
@@ -1424,7 +1240,6 @@
}
julianSkew = ((changeYear - 2000) / 400) + julianError()
- ((changeYear - 2000) / 100);
- isCached = false;
int dayOfYear = cal.get(DAY_OF_YEAR);
if (dayOfYear < julianSkew) {
currentYearSkew = dayOfYear-1;
@@ -1433,30 +1248,14 @@
lastYearSkew = 0;
currentYearSkew = julianSkew;
}
- isCached = false;
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
}
- private void readObject(ObjectInputStream stream) throws IOException,
- ClassNotFoundException {
-
+ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
setGregorianChange(new Date(gregorianCutover));
- isCached = false;
- }
-
- @Override
- public void setFirstDayOfWeek(int value) {
- super.setFirstDayOfWeek(value);
- isCached = false;
- }
-
- @Override
- public void setMinimalDaysInFirstWeek(int value) {
- super.setMinimalDaysInFirstWeek(value);
- isCached = false;
}
}
diff --git a/luni/src/main/java/java/util/Hashtable.java b/luni/src/main/java/java/util/Hashtable.java
index cea29da..a4e24bc 100644
--- a/luni/src/main/java/java/util/Hashtable.java
+++ b/luni/src/main/java/java/util/Hashtable.java
@@ -313,7 +313,7 @@
*/
public synchronized boolean containsValue(Object value) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
HashtableEntry[] tab = table;
@@ -361,8 +361,10 @@
* @see java.lang.Object#equals
*/
public synchronized V put(K key, V value) {
- if (value == null) {
- throw new NullPointerException();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (value == null) {
+ throw new NullPointerException("value == null");
}
int hash = secondaryHash(key.hashCode());
HashtableEntry<K, V>[] tab = table;
@@ -395,8 +397,10 @@
* ensure that capacity is sufficient, and does not increment modCount.
*/
private void constructorPut(K key, V value) {
- if (value == null) {
- throw new NullPointerException();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (value == null) {
+ throw new NullPointerException("value == null");
}
int hash = secondaryHash(key.hashCode());
HashtableEntry<K, V>[] tab = table;
@@ -680,7 +684,7 @@
public final V setValue(V value) {
if (value == null) {
- throw new NullPointerException();
+ throw new NullPointerException("value == null");
}
V oldValue = this.value;
this.value = value;
diff --git a/luni/src/main/java/java/util/IllegalFormatConversionException.java b/luni/src/main/java/java/util/IllegalFormatConversionException.java
index 31c0eed..af986f6 100644
--- a/luni/src/main/java/java/util/IllegalFormatConversionException.java
+++ b/luni/src/main/java/java/util/IllegalFormatConversionException.java
@@ -46,7 +46,7 @@
public IllegalFormatConversionException(char c, Class<?> arg) {
this.c = c;
if (arg == null) {
- throw new NullPointerException();
+ throw new NullPointerException("arg == null");
}
this.arg = arg;
}
diff --git a/luni/src/main/java/java/util/IllegalFormatFlagsException.java b/luni/src/main/java/java/util/IllegalFormatFlagsException.java
index 6947912..4946c55 100644
--- a/luni/src/main/java/java/util/IllegalFormatFlagsException.java
+++ b/luni/src/main/java/java/util/IllegalFormatFlagsException.java
@@ -38,7 +38,7 @@
*/
public IllegalFormatFlagsException(String flags) {
if (flags == null) {
- throw new NullPointerException();
+ throw new NullPointerException("flags == null");
}
this.flags = flags;
}
diff --git a/luni/src/main/java/java/util/ListResourceBundle.java b/luni/src/main/java/java/util/ListResourceBundle.java
index 1508b93..fc6ab97 100644
--- a/luni/src/main/java/java/util/ListResourceBundle.java
+++ b/luni/src/main/java/java/util/ListResourceBundle.java
@@ -108,7 +108,7 @@
public final Object handleGetObject(String key) {
initializeTable();
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
return table.get(key);
}
diff --git a/luni/src/main/java/java/util/MissingFormatArgumentException.java b/luni/src/main/java/java/util/MissingFormatArgumentException.java
index ce72efa..1733501 100644
--- a/luni/src/main/java/java/util/MissingFormatArgumentException.java
+++ b/luni/src/main/java/java/util/MissingFormatArgumentException.java
@@ -37,7 +37,7 @@
*/
public MissingFormatArgumentException(String s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
this.s = s;
}
diff --git a/luni/src/main/java/java/util/MissingFormatWidthException.java b/luni/src/main/java/java/util/MissingFormatWidthException.java
index b6d0ca6..0a3b5ae 100644
--- a/luni/src/main/java/java/util/MissingFormatWidthException.java
+++ b/luni/src/main/java/java/util/MissingFormatWidthException.java
@@ -36,7 +36,7 @@
*/
public MissingFormatWidthException(String s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
this.s = s;
}
diff --git a/luni/src/main/java/java/util/NavigableMap.java b/luni/src/main/java/java/util/NavigableMap.java
index 29961c8..beeb651 100644
--- a/luni/src/main/java/java/util/NavigableMap.java
+++ b/luni/src/main/java/java/util/NavigableMap.java
@@ -1,14 +1,13 @@
/*
* Written by Doug Lea and Josh Bloch with assistance from members of JCP
* JSR-166 Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
// BEGIN android-note
// removed link to collections framework docs
-// changed {@link #subMap(Object)} to {@link #subMap} to satisfy DroidDoc
// END android-note
/**
@@ -48,9 +47,9 @@
* method {@code put}.
*
* <p>Methods
- * {@link #subMap subMap(K, K)},
- * {@link #headMap headMap(K)}, and
- * {@link #tailMap tailMap(K)}
+ * {@link #subMap(Object, Object) subMap(K, K)},
+ * {@link #headMap(Object) headMap(K)}, and
+ * {@link #tailMap(Object) tailMap(K)}
* are specified to return {@code SortedMap} to allow existing
* implementations of {@code SortedMap} to be compatibly retrofitted to
* implement {@code NavigableMap}, but extensions and implementations
diff --git a/luni/src/main/java/java/util/NavigableSet.java b/luni/src/main/java/java/util/NavigableSet.java
index cff0800..f410313 100644
--- a/luni/src/main/java/java/util/NavigableSet.java
+++ b/luni/src/main/java/java/util/NavigableSet.java
@@ -1,14 +1,13 @@
/*
* Written by Doug Lea and Josh Bloch with assistance from members of JCP
* JSR-166 Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
// BEGIN android-note
// removed link to collections framework docs
-// changed {@link #subSet(Object)} to {@link #subSet} to satisfy DroidDoc
// END android-note
/**
@@ -41,9 +40,9 @@
* Comparable} elements intrinsically do not permit {@code null}.)
*
* <p>Methods
- * {@link #subSet subSet(E, E)},
- * {@link #headSet headSet(E)}, and
- * {@link #tailSet tailSet(E)}
+ * {@link #subSet(Object, Object) subSet(E, E)},
+ * {@link #headSet(Object) headSet(E)}, and
+ * {@link #tailSet(Object) tailSet(E)}
* are specified to return {@code SortedSet} to allow existing
* implementations of {@code SortedSet} to be compatibly retrofitted to
* implement {@code NavigableSet}, but extensions and implementations
diff --git a/luni/src/main/java/java/util/Observable.java b/luni/src/main/java/java/util/Observable.java
index 2c2877e..c984c68 100644
--- a/luni/src/main/java/java/util/Observable.java
+++ b/luni/src/main/java/java/util/Observable.java
@@ -49,7 +49,7 @@
*/
public void addObserver(Observer observer) {
if (observer == null) {
- throw new NullPointerException();
+ throw new NullPointerException("observer == null");
}
synchronized (this) {
if (!observers.contains(observer))
diff --git a/luni/src/main/java/java/util/PriorityQueue.java b/luni/src/main/java/java/util/PriorityQueue.java
index 10c5968..e09eb05 100644
--- a/luni/src/main/java/java/util/PriorityQueue.java
+++ b/luni/src/main/java/java/util/PriorityQueue.java
@@ -186,7 +186,7 @@
*/
public boolean offer(E o) {
if (o == null) {
- throw new NullPointerException();
+ throw new NullPointerException("o == null");
}
growToSize(size + 1);
elements[size] = o;
@@ -387,7 +387,7 @@
private void initSize(Collection<? extends E> c) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
if (c.isEmpty()) {
elements = newElementArray(1);
diff --git a/luni/src/main/java/java/util/Properties.java b/luni/src/main/java/java/util/Properties.java
index 1731ad8..57c6a00 100644
--- a/luni/src/main/java/java/util/Properties.java
+++ b/luni/src/main/java/java/util/Properties.java
@@ -243,7 +243,7 @@
*/
public synchronized void load(InputStream in) throws IOException {
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("in == null");
}
load(new InputStreamReader(in, "ISO-8859-1"));
}
@@ -276,7 +276,7 @@
@SuppressWarnings("fallthrough")
public synchronized void load(Reader in) throws IOException {
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("in == null");
}
int mode = NONE, unicode = 0, count = 0;
char nextChar, buf[] = new char[40];
@@ -578,7 +578,7 @@
public synchronized void loadFromXML(InputStream in) throws IOException,
InvalidPropertiesFormatException {
if (in == null) {
- throw new NullPointerException();
+ throw new NullPointerException("in == null");
}
if (builder == null) {
@@ -690,8 +690,10 @@
public synchronized void storeToXML(OutputStream os, String comment,
String encoding) throws IOException {
- if (os == null || encoding == null) {
- throw new NullPointerException();
+ if (os == null) {
+ throw new NullPointerException("os == null");
+ } else if (encoding == null) {
+ throw new NullPointerException("encoding == null");
}
/*
diff --git a/luni/src/main/java/java/util/PropertyResourceBundle.java b/luni/src/main/java/java/util/PropertyResourceBundle.java
index 4029ee1..dbbd139 100644
--- a/luni/src/main/java/java/util/PropertyResourceBundle.java
+++ b/luni/src/main/java/java/util/PropertyResourceBundle.java
@@ -46,7 +46,7 @@
*/
public PropertyResourceBundle(InputStream stream) throws IOException {
if (stream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("stream == null");
}
resources = new Properties();
resources.load(stream);
diff --git a/luni/src/main/java/java/util/Queue.java b/luni/src/main/java/java/util/Queue.java
index 5aef944..8b465e6 100644
--- a/luni/src/main/java/java/util/Queue.java
+++ b/luni/src/main/java/java/util/Queue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util;
diff --git a/luni/src/main/java/java/util/Random.java b/luni/src/main/java/java/util/Random.java
index 7ce74cc..b0a92ff 100644
--- a/luni/src/main/java/java/util/Random.java
+++ b/luni/src/main/java/java/util/Random.java
@@ -140,25 +140,23 @@
* section 3.4.1, subsection C, algorithm P.
*/
public synchronized double nextGaussian() {
- if (haveNextNextGaussian) { // if X1 has been returned, return the
- // second Gaussian
+ if (haveNextNextGaussian) {
haveNextNextGaussian = false;
return nextNextGaussian;
}
double v1, v2, s;
do {
- v1 = 2 * nextDouble() - 1; // Generates two independent random
- // variables U1, U2
+ v1 = 2 * nextDouble() - 1;
v2 = 2 * nextDouble() - 1;
s = v1 * v1 + v2 * v2;
- } while (s >= 1);
- double norm = Math.sqrt(-2 * Math.log(s) / s);
- nextNextGaussian = v2 * norm; // should that not be norm instead
- // of multiplier ?
+ } while (s >= 1 || s == 0);
+
+ // The specification says this uses StrictMath.
+ double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
+ nextNextGaussian = v2 * multiplier;
haveNextNextGaussian = true;
- return v1 * norm; // should that not be norm instead of multiplier
- // ?
+ return v1 * multiplier;
}
/**
diff --git a/luni/src/main/java/java/util/ResourceBundle.java b/luni/src/main/java/java/util/ResourceBundle.java
index ff38b5b..f5c8285 100644
--- a/luni/src/main/java/java/util/ResourceBundle.java
+++ b/luni/src/main/java/java/util/ResourceBundle.java
@@ -211,8 +211,10 @@
*/
public static ResourceBundle getBundle(String bundleName, Locale locale,
ClassLoader loader) throws MissingResourceException {
- if (loader == null || bundleName == null) {
- throw new NullPointerException();
+ if (loader == null) {
+ throw new NullPointerException("loader == null");
+ } else if (bundleName == null) {
+ throw new NullPointerException("bundleName == null");
}
Locale defaultLocale = Locale.getDefault();
if (!cacheLocale.equals(defaultLocale)) {
@@ -610,14 +612,14 @@
public static void clearCache(ClassLoader loader) {
if (loader == null) {
- throw new NullPointerException();
+ throw new NullPointerException("loader == null");
}
cache.remove(loader);
}
public boolean containsKey(String key) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
return keySet().contains(key);
}
@@ -665,8 +667,10 @@
@Override
public Locale getFallbackLocale(String baseName, Locale locale) {
- if (baseName == null || locale == null) {
- throw new NullPointerException();
+ if (baseName == null) {
+ throw new NullPointerException("baseName == null");
+ } else if (locale == null) {
+ throw new NullPointerException("locale == null");
}
return null;
}
@@ -804,8 +808,10 @@
* {@code locale}.
*/
public List<Locale> getCandidateLocales(String baseName, Locale locale) {
- if (baseName == null || locale == null) {
- throw new NullPointerException();
+ if (baseName == null) {
+ throw new NullPointerException("baseName == null");
+ } else if (locale == null) {
+ throw new NullPointerException("locale == null");
}
List<Locale> retList = new ArrayList<Locale>();
String language = locale.getLanguage();
@@ -829,7 +835,7 @@
*/
public List<String> getFormats(String baseName) {
if (baseName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("baseName == null");
}
return format;
}
@@ -838,8 +844,10 @@
* Returns the fallback locale for {@code baseName} in {@code locale}.
*/
public Locale getFallbackLocale(String baseName, Locale locale) {
- if (baseName == null || locale == null) {
- throw new NullPointerException();
+ if (baseName == null) {
+ throw new NullPointerException("baseName == null");
+ } else if (locale == null) {
+ throw new NullPointerException("locale == null");
}
if (Locale.getDefault() != locale) {
return Locale.getDefault();
@@ -872,8 +880,10 @@
String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException,
IOException {
- if (format == null || loader == null) {
- throw new NullPointerException();
+ if (format == null) {
+ throw new NullPointerException("format == null");
+ } else if (loader == null) {
+ throw new NullPointerException("loader == null");
}
final String bundleName = toBundleName(baseName, locale);
final ClassLoader clsloader = loader;
@@ -938,8 +948,10 @@
* default is TTL_NO_EXPIRATION_CONTROL.
*/
public long getTimeToLive(String baseName, Locale locale) {
- if (baseName == null || locale == null) {
- throw new NullPointerException();
+ if (baseName == null) {
+ throw new NullPointerException("baseName == null");
+ } else if (locale == null) {
+ throw new NullPointerException("locale == null");
}
return TTL_NO_EXPIRATION_CONTROL;
}
@@ -966,7 +978,7 @@
long loadTime) {
if (bundle == null) {
// FIXME what's the use of bundle?
- throw new NullPointerException();
+ throw new NullPointerException("bundle == null");
}
String bundleName = toBundleName(baseName, locale);
String suffix = format;
@@ -1004,7 +1016,7 @@
final String preString = UNDER_SCORE;
final String underline = UNDER_SCORE;
if (baseName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("baseName == null");
}
StringBuilder ret = new StringBuilder();
StringBuilder prefix = new StringBuilder();
@@ -1044,7 +1056,7 @@
*/
public final String toResourceName(String bundleName, String suffix) {
if (suffix == null) {
- throw new NullPointerException();
+ throw new NullPointerException("suffix == null");
}
StringBuilder ret = new StringBuilder(bundleName.replace('.', '/'));
ret.append('.');
diff --git a/luni/src/main/java/java/util/Scanner.java b/luni/src/main/java/java/util/Scanner.java
index 8f889b3..5f7d0e3 100644
--- a/luni/src/main/java/java/util/Scanner.java
+++ b/luni/src/main/java/java/util/Scanner.java
@@ -241,7 +241,7 @@
*/
public Scanner(Readable src) {
if (src == null) {
- throw new NullPointerException();
+ throw new NullPointerException("src == null");
}
input = src;
initialization();
@@ -1664,7 +1664,7 @@
*/
public Scanner useLocale(Locale l) {
if (l == null) {
- throw new NullPointerException();
+ throw new NullPointerException("l == null");
}
this.locale = l;
return this;
@@ -1724,7 +1724,7 @@
*/
private void checkNull(Pattern pattern) {
if (pattern == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pattern == null");
}
}
diff --git a/luni/src/main/java/java/util/ServiceLoader.java b/luni/src/main/java/java/util/ServiceLoader.java
index beacaab..016ab3f 100644
--- a/luni/src/main/java/java/util/ServiceLoader.java
+++ b/luni/src/main/java/java/util/ServiceLoader.java
@@ -78,7 +78,7 @@
// It makes no sense for service to be null.
// classLoader is null if you want the system class loader.
if (service == null) {
- throw new NullPointerException();
+ throw new NullPointerException("service == null");
}
this.service = service;
this.classLoader = classLoader;
diff --git a/luni/src/main/java/java/util/SimpleTimeZone.java b/luni/src/main/java/java/util/SimpleTimeZone.java
index 6c9b443..93dc88e 100644
--- a/luni/src/main/java/java/util/SimpleTimeZone.java
+++ b/luni/src/main/java/java/util/SimpleTimeZone.java
@@ -217,10 +217,17 @@
throw new IllegalArgumentException("Invalid daylightSavings: " + daylightSavings);
}
dstSavings = daylightSavings;
- // TODO: do we need to set useDaylight is dstSavings != 0?
- setStartRule(startMonth, startDay, startDayOfWeek, startTime);
- setEndRule(endMonth, endDay, endDayOfWeek, endTime);
+ this.startMonth = startMonth;
+ this.startDay = startDay;
+ this.startDayOfWeek = startDayOfWeek;
+ this.startTime = startTime;
+ setStartMode();
+ this.endMonth = endMonth;
+ this.endDay = endDay;
+ this.endDayOfWeek = endDayOfWeek;
+ this.endTime = endTime;
+ setEndMode();
}
/**
diff --git a/luni/src/main/java/java/util/StringTokenizer.java b/luni/src/main/java/java/util/StringTokenizer.java
index 1b07604..e6686e8 100644
--- a/luni/src/main/java/java/util/StringTokenizer.java
+++ b/luni/src/main/java/java/util/StringTokenizer.java
@@ -91,13 +91,13 @@
*/
public StringTokenizer(String string, String delimiters,
boolean returnDelimiters) {
- if (string != null) {
- this.string = string;
- this.delimiters = delimiters;
- this.returnDelimiters = returnDelimiters;
- this.position = 0;
- } else
- throw new NullPointerException();
+ if (string == null) {
+ throw new NullPointerException("string == null");
+ }
+ this.string = string;
+ this.delimiters = delimiters;
+ this.returnDelimiters = returnDelimiters;
+ this.position = 0;
}
/**
@@ -143,7 +143,7 @@
*/
public boolean hasMoreTokens() {
if (delimiters == null) {
- throw new NullPointerException();
+ throw new NullPointerException("delimiters == null");
}
int length = string.length();
if (position < length) {
@@ -180,7 +180,7 @@
*/
public String nextToken() {
if (delimiters == null) {
- throw new NullPointerException();
+ throw new NullPointerException("delimiters == null");
}
int i = position;
int length = string.length();
diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java
index 34763cc..85011bc 100644
--- a/luni/src/main/java/java/util/TimeZone.java
+++ b/luni/src/main/java/java/util/TimeZone.java
@@ -17,7 +17,10 @@
package java.util;
+import java.io.IOException;
import java.io.Serializable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import libcore.icu.TimeZones;
import libcore.util.ZoneInfoDB;
@@ -28,7 +31,7 @@
* <p>Most applications will use {@link #getDefault} which returns a {@code TimeZone} based on
* the time zone where the program is running.
*
- * <p>You can also get a specific {@code TimeZone} {@link #getTimeZone by id}.
+ * <p>You can also get a specific {@code TimeZone} {@link #getTimeZone by Olson ID}.
*
* <p>It is highly unlikely you'll ever want to use anything but the factory methods yourself.
* Let classes like {@link Calendar} and {@link java.text.SimpleDateFormat} do the date
@@ -64,6 +67,8 @@
public abstract class TimeZone implements Serializable, Cloneable {
private static final long serialVersionUID = 3581463369166924961L;
+ private static final Pattern CUSTOM_ZONE_ID_PATTERN = Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$");
+
/**
* The short display name style, such as {@code PDT}. Requests for this
* style may yield GMT offsets like {@code GMT-08:00}.
@@ -76,7 +81,8 @@
*/
public static final int LONG = 1;
- static final TimeZone GMT = new SimpleTimeZone(0, "GMT"); // Greenwich Mean Time
+ private static final TimeZone GMT = new SimpleTimeZone(0, "GMT");
+ private static final TimeZone UTC = new SimpleTimeZone(0, "UTC");
private static TimeZone defaultTimeZone;
@@ -214,16 +220,27 @@
}
/**
- * Returns the daylight savings offset in milliseconds for this time zone.
- * The base implementation returns {@code 3600000} (1 hour) for time zones
- * that use daylight savings time and {@code 0} for timezones that do not.
- * Subclasses should override this method for other daylight savings
- * offsets.
+ * Returns the latest daylight savings in milliseconds for this time zone, relative
+ * to this time zone's regular UTC offset (as returned by {@link #getRawOffset}).
*
- * <p>Note that this method doesn't tell you whether or not to apply the
+ * <p>This class returns {@code 3600000} (1 hour) for time zones
+ * that use daylight savings time and {@code 0} for timezones that do not,
+ * leaving it to subclasses to override this method for other daylight savings
+ * offsets. (There are time zones, such as {@code Australia/Lord_Howe},
+ * that use other values.)
+ *
+ * <p>Note that this method doesn't tell you whether or not to <i>apply</i> the
* offset: you need to call {@code inDaylightTime} for the specific time
* you're interested in. If this method returns a non-zero offset, that only
* tells you that this {@code TimeZone} sometimes observes daylight savings.
+ *
+ * <p>Note also that this method doesn't necessarily return the value you need
+ * to apply to the time you're working with. This value can and does change over
+ * time for a given time zone.
+ *
+ * <p>It's highly unlikely that you should ever call this method. You
+ * probably want {@link #getOffset} instead, which tells you the offset
+ * for a specific point in time, and takes daylight savings into account for you.
*/
public int getDSTSavings() {
return useDaylightTime() ? 3600000 : 0;
@@ -265,17 +282,19 @@
public abstract int getRawOffset();
/**
- * Returns a {@code TimeZone} suitable for {@code id}, or {@code GMT} for unknown ids.
+ * Returns a {@code TimeZone} corresponding to the given {@code id}, or {@code GMT}
+ * for unknown ids.
*
- * <p>An id can be an Olson name of the form <i>Area</i>/<i>Location</i>, such
+ * <p>An ID can be an Olson name of the form <i>Area</i>/<i>Location</i>, such
* as {@code America/Los_Angeles}. The {@link #getAvailableIDs} method returns
* the supported names.
*
- * <p>This method can also create a custom {@code TimeZone} using the following
- * syntax: {@code GMT[+|-]hh[[:]mm]}. For example, {@code TimeZone.getTimeZone("GMT+14:00")}
- * would return an object with a raw offset of +14 hours from UTC, and which does <i>not</i>
- * use daylight savings. These are rarely useful, because they don't correspond to time
- * zones actually in use.
+ * <p>This method can also create a custom {@code TimeZone} given an ID with the following
+ * syntax: {@code GMT[+|-]hh[[:]mm]}. For example, {@code "GMT+05:00"}, {@code "GMT+0500"},
+ * {@code "GMT+5:00"}, {@code "GMT+500"}, {@code "GMT+05"}, and {@code "GMT+5"} all return
+ * an object with a raw offset of +5 hours from UTC, and which does <i>not</i> use daylight
+ * savings. These are rarely useful, because they don't correspond to time zones actually
+ * in use by humans.
*
* <p>Other than the special cases "UTC" and "GMT" (which are synonymous in this context,
* both corresponding to UTC), Android does not support the deprecated three-letter time
@@ -285,80 +304,66 @@
if (id == null) {
throw new NullPointerException("id == null");
}
- TimeZone zone = ZoneInfoDB.getTimeZone(id);
- if (zone != null) {
- return zone;
+
+ // Special cases? These can clone an existing instance.
+ // TODO: should we just add a cache to ZoneInfoDB instead?
+ if (id.length() == 3) {
+ if (id.equals("GMT")) {
+ return (TimeZone) GMT.clone();
+ }
+ if (id.equals("UTC")) {
+ return (TimeZone) UTC.clone();
+ }
}
+
+ // In the database?
+ TimeZone zone = null;
+ try {
+ zone = ZoneInfoDB.makeTimeZone(id);
+ } catch (IOException ignored) {
+ }
+
+ // Custom time zone?
if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
zone = getCustomTimeZone(id);
}
- if (zone == null) {
- zone = (TimeZone) GMT.clone();
- }
- return zone;
+
+ // We never return null; on failure we return the equivalent of "GMT".
+ return (zone != null) ? zone : (TimeZone) GMT.clone();
}
/**
- * Returns a new SimpleTimeZone for an id of the form "GMT[+|-]hh[[:]mm]", or null.
+ * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null.
*/
private static TimeZone getCustomTimeZone(String id) {
- char sign = id.charAt(3);
- if (sign != '+' && sign != '-') {
+ Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id);
+ if (!m.matches()) {
return null;
}
- int[] position = new int[1];
- String formattedName = formatTimeZoneName(id, 4);
- int hour = parseNumber(formattedName, 4, position);
- if (hour < 0 || hour > 23) {
- return null;
- }
- int index = position[0];
- if (index == -1) {
- return null;
- }
- int raw = hour * 3600000;
- if (index < formattedName.length() && formattedName.charAt(index) == ':') {
- int minute = parseNumber(formattedName, index + 1, position);
- if (position[0] == -1 || minute < 0 || minute > 59) {
- return null;
+
+ int hour;
+ int minute = 0;
+ try {
+ hour = Integer.parseInt(m.group(1));
+ if (m.group(3) != null) {
+ minute = Integer.parseInt(m.group(3));
}
- raw += minute * 60000;
- } else if (hour >= 30 || index > 6) {
- raw = (hour / 100 * 3600000) + (hour % 100 * 60000);
+ } catch (NumberFormatException impossible) {
+ throw new AssertionError(impossible);
}
+
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
+ return null;
+ }
+
+ char sign = id.charAt(3);
+ int raw = (hour * 3600000) + (minute * 60000);
if (sign == '-') {
raw = -raw;
}
- return new SimpleTimeZone(raw, formattedName);
- }
- private static String formatTimeZoneName(String name, int offset) {
- StringBuilder buf = new StringBuilder();
- int index = offset, length = name.length();
- buf.append(name.substring(0, offset));
-
- while (index < length) {
- if (Character.digit(name.charAt(index), 10) != -1) {
- buf.append(name.charAt(index));
- if ((length - (index + 1)) == 2) {
- buf.append(':');
- }
- } else if (name.charAt(index) == ':') {
- buf.append(':');
- }
- index++;
- }
-
- if (buf.toString().indexOf(":") == -1) {
- buf.append(':');
- buf.append("00");
- }
-
- if (buf.toString().indexOf(":") == 5) {
- buf.insert(4, '0');
- }
-
- return buf.toString();
+ String cleanId = String.format("GMT%c%02d:%02d", sign, hour, minute);
+ return new SimpleTimeZone(raw, cleanId);
}
/**
@@ -380,17 +385,6 @@
*/
public abstract boolean inDaylightTime(Date time);
- private static int parseNumber(String string, int offset, int[] position) {
- int index = offset, length = string.length(), digit, result = 0;
- while (index < length
- && (digit = Character.digit(string.charAt(index), 10)) != -1) {
- index++;
- result = result * 10 + digit;
- }
- position[0] = index == offset ? -1 : index;
- return result;
- }
-
/**
* Overrides the default time zone for the current process only.
*
@@ -411,7 +405,7 @@
*/
public void setID(String id) {
if (id == null) {
- throw new NullPointerException();
+ throw new NullPointerException("id == null");
}
ID = id;
}
diff --git a/luni/src/main/java/java/util/Timer.java b/luni/src/main/java/java/util/Timer.java
index b4f7a83..afc745c 100644
--- a/luni/src/main/java/java/util/Timer.java
+++ b/luni/src/main/java/java/util/Timer.java
@@ -362,7 +362,7 @@
*/
public Timer(String name, boolean isDaemon) {
if (name == null) {
- throw new NullPointerException("name is null");
+ throw new NullPointerException("name == null");
}
this.impl = new TimerImpl(name, isDaemon);
this.finalizer = new FinalizerHelper(impl);
diff --git a/luni/src/main/java/java/util/UUID.java b/luni/src/main/java/java/util/UUID.java
index a932bb2..8ac0a63 100644
--- a/luni/src/main/java/java/util/UUID.java
+++ b/luni/src/main/java/java/util/UUID.java
@@ -142,7 +142,7 @@
*/
public static UUID nameUUIDFromBytes(byte[] name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
@@ -179,7 +179,7 @@
*/
public static UUID fromString(String uuid) {
if (uuid == null) {
- throw new NullPointerException();
+ throw new NullPointerException("uuid == null");
}
int[] position = new int[5];
diff --git a/luni/src/main/java/java/util/UnknownFormatConversionException.java b/luni/src/main/java/java/util/UnknownFormatConversionException.java
index e26de91..29af4e1 100644
--- a/luni/src/main/java/java/util/UnknownFormatConversionException.java
+++ b/luni/src/main/java/java/util/UnknownFormatConversionException.java
@@ -35,7 +35,7 @@
*/
public UnknownFormatConversionException(String s) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
this.s = s;
}
diff --git a/luni/src/main/java/java/util/UnknownFormatFlagsException.java b/luni/src/main/java/java/util/UnknownFormatFlagsException.java
index 9daa3f1..990432b 100644
--- a/luni/src/main/java/java/util/UnknownFormatFlagsException.java
+++ b/luni/src/main/java/java/util/UnknownFormatFlagsException.java
@@ -37,7 +37,7 @@
*/
public UnknownFormatFlagsException(String f) {
if (f == null) {
- throw new NullPointerException();
+ throw new NullPointerException("f == null");
}
flags = f;
}
diff --git a/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java b/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
index 36fcecc..a7f7745 100644
--- a/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
index a622832..e30ab67 100644
--- a/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
@@ -1,12 +1,17 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.locks.*;
-import java.util.*;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.AbstractQueue;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.lang.ref.WeakReference;
// BEGIN android-note
// removed link to collections framework docs
@@ -73,11 +78,20 @@
/** Main lock guarding all access */
final ReentrantLock lock;
+
/** Condition for waiting takes */
private final Condition notEmpty;
+
/** Condition for waiting puts */
private final Condition notFull;
+ /**
+ * Shared state for currently active iterators, or null if there
+ * are known not to be any. Allows queue operations to update
+ * iterator state.
+ */
+ transient Itrs itrs = null;
+
// Internal helper methods
/**
@@ -94,16 +108,12 @@
return ((i == 0) ? items.length : i) - 1;
}
- @SuppressWarnings("unchecked")
- static <E> E cast(Object item) {
- return (E) item;
- }
-
/**
* Returns item at index i.
*/
final E itemAt(int i) {
- return this.<E>cast(items[i]);
+ @SuppressWarnings("unchecked") E x = (E) items[i];
+ return x;
}
/**
@@ -120,10 +130,12 @@
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
- private void insert(E x) {
+ private void enqueue(E x) {
+ // assert lock.getHoldCount() == 1;
+ // assert items[putIndex] == null;
items[putIndex] = x;
putIndex = inc(putIndex);
- ++count;
+ count++;
notEmpty.signal();
}
@@ -131,42 +143,57 @@
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
- private E extract() {
+ private E dequeue() {
+ // assert lock.getHoldCount() == 1;
+ // assert items[takeIndex] != null;
final Object[] items = this.items;
- E x = this.<E>cast(items[takeIndex]);
+ @SuppressWarnings("unchecked") E x = (E) items[takeIndex];
items[takeIndex] = null;
takeIndex = inc(takeIndex);
- --count;
+ count--;
+ if (itrs != null)
+ itrs.elementDequeued();
notFull.signal();
return x;
}
/**
- * Deletes item at position i.
- * Utility for remove and iterator.remove.
+ * Deletes item at array index removeIndex.
+ * Utility for remove(Object) and iterator.remove.
* Call only when holding lock.
*/
- void removeAt(int i) {
+ void removeAt(final int removeIndex) {
+ // assert lock.getHoldCount() == 1;
+ // assert items[removeIndex] != null;
+ // assert removeIndex >= 0 && removeIndex < items.length;
final Object[] items = this.items;
- // if removing front item, just advance
- if (i == takeIndex) {
+ if (removeIndex == takeIndex) {
+ // removing front item; just advance
items[takeIndex] = null;
takeIndex = inc(takeIndex);
+ count--;
+ if (itrs != null)
+ itrs.elementDequeued();
} else {
+ // an "interior" remove
+
// slide over all others up through putIndex.
- for (;;) {
- int nexti = inc(i);
- if (nexti != putIndex) {
- items[i] = items[nexti];
- i = nexti;
+ final int putIndex = this.putIndex;
+ for (int i = removeIndex;;) {
+ int next = inc(i);
+ if (next != putIndex) {
+ items[i] = items[next];
+ i = next;
} else {
items[i] = null;
- putIndex = i;
+ this.putIndex = i;
break;
}
}
+ count--;
+ if (itrs != null)
+ itrs.removedAt(removeIndex);
}
- --count;
notFull.signal();
}
@@ -271,7 +298,7 @@
if (count == items.length)
return false;
else {
- insert(e);
+ enqueue(e);
return true;
}
} finally {
@@ -293,7 +320,7 @@
try {
while (count == items.length)
notFull.await();
- insert(e);
+ enqueue(e);
} finally {
lock.unlock();
}
@@ -320,7 +347,7 @@
return false;
nanos = notFull.awaitNanos(nanos);
}
- insert(e);
+ enqueue(e);
return true;
} finally {
lock.unlock();
@@ -331,7 +358,7 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- return (count == 0) ? null : extract();
+ return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
@@ -343,7 +370,7 @@
try {
while (count == 0)
notEmpty.await();
- return extract();
+ return dequeue();
} finally {
lock.unlock();
}
@@ -359,7 +386,7 @@
return null;
nanos = notEmpty.awaitNanos(nanos);
}
- return extract();
+ return dequeue();
} finally {
lock.unlock();
}
@@ -438,11 +465,15 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) {
- if (o.equals(items[i])) {
- removeAt(i);
- return true;
- }
+ if (count > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ if (o.equals(items[i])) {
+ removeAt(i);
+ return true;
+ }
+ } while ((i = inc(i)) != putIndex);
}
return false;
} finally {
@@ -464,9 +495,14 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--)
- if (o.equals(items[i]))
- return true;
+ if (count > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ if (o.equals(items[i]))
+ return true;
+ } while ((i = inc(i)) != putIndex);
+ }
return false;
} finally {
lock.unlock();
@@ -522,8 +558,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -589,12 +624,20 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--)
- items[i] = null;
- count = 0;
- putIndex = 0;
- takeIndex = 0;
- notFull.signalAll();
+ int k = count;
+ if (k > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ items[i] = null;
+ } while ((i = inc(i)) != putIndex);
+ takeIndex = putIndex;
+ count = 0;
+ if (itrs != null)
+ itrs.queueIsEmpty();
+ for (; k > 0 && lock.hasWaiters(notFull); k--)
+ notFull.signal();
+ }
} finally {
lock.unlock();
}
@@ -607,32 +650,7 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
- checkNotNull(c);
- if (c == this)
- throw new IllegalArgumentException();
- final Object[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = takeIndex;
- int n = 0;
- int max = count;
- while (n < max) {
- c.add(this.<E>cast(items[i]));
- items[i] = null;
- i = inc(i);
- ++n;
- }
- if (n > 0) {
- count = 0;
- putIndex = 0;
- takeIndex = 0;
- notFull.signalAll();
- }
- return n;
- } finally {
- lock.unlock();
- }
+ return drainTo(c, Integer.MAX_VALUE);
}
/**
@@ -651,21 +669,33 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- int i = takeIndex;
- int n = 0;
- int max = (maxElements < count) ? maxElements : count;
- while (n < max) {
- c.add(this.<E>cast(items[i]));
- items[i] = null;
- i = inc(i);
- ++n;
+ int n = Math.min(maxElements, count);
+ int take = takeIndex;
+ int i = 0;
+ try {
+ while (i < n) {
+ @SuppressWarnings("unchecked") E x = (E) items[take];
+ c.add(x);
+ items[take] = null;
+ take = inc(take);
+ i++;
+ }
+ return n;
+ } finally {
+ // Restore invariants even if c.add() threw
+ if (i > 0) {
+ count -= i;
+ takeIndex = take;
+ if (itrs != null) {
+ if (count == 0)
+ itrs.queueIsEmpty();
+ else if (i > take)
+ itrs.takeIndexWrapped();
+ }
+ for (; i > 0 && lock.hasWaiters(notFull); i--)
+ notFull.signal();
+ }
}
- if (n > 0) {
- count -= n;
- takeIndex = i;
- notFull.signalAll();
- }
- return n;
} finally {
lock.unlock();
}
@@ -675,12 +705,12 @@
* Returns an iterator over the elements in this queue in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
- * <p>The returned {@code Iterator} is a "weakly consistent" iterator that
+ * <p>The returned iterator is a "weakly consistent" iterator that
* will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * ConcurrentModificationException}, and guarantees to traverse
+ * elements as they existed upon construction of the iterator, and
+ * may (but is not guaranteed to) reflect any modifications
+ * subsequent to construction.
*
* @return an iterator over the elements in this queue in proper sequence
*/
@@ -689,88 +719,627 @@
}
/**
- * Iterator for ArrayBlockingQueue. To maintain weak consistency
- * with respect to puts and takes, we (1) read ahead one slot, so
- * as to not report hasNext true but then not have an element to
- * return -- however we later recheck this slot to use the most
- * current value; (2) ensure that each array slot is traversed at
- * most once (by tracking "remaining" elements); (3) skip over
- * null slots, which can occur if takes race ahead of iterators.
- * However, for circular array-based queues, we cannot rely on any
- * well established definition of what it means to be weakly
- * consistent with respect to interior removes since these may
- * require slot overwrites in the process of sliding elements to
- * cover gaps. So we settle for resiliency, operating on
- * established apparent nexts, which may miss some elements that
- * have moved between calls to next.
+ * Shared data between iterators and their queue, allowing queue
+ * modifications to update iterators when elements are removed.
+ *
+ * This adds a lot of complexity for the sake of correctly
+ * handling some uncommon operations, but the combination of
+ * circular-arrays and supporting interior removes (i.e., those
+ * not at head) would cause iterators to sometimes lose their
+ * places and/or (re)report elements they shouldn't. To avoid
+ * this, when a queue has one or more iterators, it keeps iterator
+ * state consistent by:
+ *
+ * (1) keeping track of the number of "cycles", that is, the
+ * number of times takeIndex has wrapped around to 0.
+ * (2) notifying all iterators via the callback removedAt whenever
+ * an interior element is removed (and thus other elements may
+ * be shifted).
+ *
+ * These suffice to eliminate iterator inconsistencies, but
+ * unfortunately add the secondary responsibility of maintaining
+ * the list of iterators. We track all active iterators in a
+ * simple linked list (accessed only when the queue's lock is
+ * held) of weak references to Itr. The list is cleaned up using
+ * 3 different mechanisms:
+ *
+ * (1) Whenever a new iterator is created, do some O(1) checking for
+ * stale list elements.
+ *
+ * (2) Whenever takeIndex wraps around to 0, check for iterators
+ * that have been unused for more than one wrap-around cycle.
+ *
+ * (3) Whenever the queue becomes empty, all iterators are notified
+ * and this entire data structure is discarded.
+ *
+ * So in addition to the removedAt callback that is necessary for
+ * correctness, iterators have the shutdown and takeIndexWrapped
+ * callbacks that help remove stale iterators from the list.
+ *
+ * Whenever a list element is examined, it is expunged if either
+ * the GC has determined that the iterator is discarded, or if the
+ * iterator reports that it is "detached" (does not need any
+ * further state updates). Overhead is maximal when takeIndex
+ * never advances, iterators are discarded before they are
+ * exhausted, and all removals are interior removes, in which case
+ * all stale iterators are discovered by the GC. But even in this
+ * case we don't increase the amortized complexity.
+ *
+ * Care must be taken to keep list sweeping methods from
+ * reentrantly invoking another such method, causing subtle
+ * corruption bugs.
*/
- private class Itr implements Iterator<E> {
- private int remaining; // Number of elements yet to be returned
- private int nextIndex; // Index of element to be returned by next
- private E nextItem; // Element to be returned by next call to next
- private E lastItem; // Element returned by last call to next
- private int lastRet; // Index of last element returned, or -1 if none
+ class Itrs {
- Itr() {
- final ReentrantLock lock = ArrayBlockingQueue.this.lock;
- lock.lock();
- try {
- lastRet = -1;
- if ((remaining = count) > 0)
- nextItem = itemAt(nextIndex = takeIndex);
- } finally {
- lock.unlock();
+ /**
+ * Node in a linked list of weak iterator references.
+ */
+ private class Node extends WeakReference<Itr> {
+ Node next;
+
+ Node(Itr iterator, Node next) {
+ super(iterator);
+ this.next = next;
}
}
- public boolean hasNext() {
- return remaining > 0;
+ /** Incremented whenever takeIndex wraps around to 0 */
+ int cycles = 0;
+
+ /** Linked list of weak iterator references */
+ private Node head;
+
+ /** Used to expunge stale iterators */
+ private Node sweeper = null;
+
+ private static final int SHORT_SWEEP_PROBES = 4;
+ private static final int LONG_SWEEP_PROBES = 16;
+
+ Itrs(Itr initial) {
+ register(initial);
}
- public E next() {
- final ReentrantLock lock = ArrayBlockingQueue.this.lock;
- lock.lock();
- try {
- if (remaining <= 0)
- throw new NoSuchElementException();
- lastRet = nextIndex;
- E x = itemAt(nextIndex); // check for fresher value
- if (x == null) {
- x = nextItem; // we are forced to report old value
- lastItem = null; // but ensure remove fails
+ /**
+ * Sweeps itrs, looking for and expunging stale iterators.
+ * If at least one was found, tries harder to find more.
+ * Called only from iterating thread.
+ *
+ * @param tryHarder whether to start in try-harder mode, because
+ * there is known to be at least one iterator to collect
+ */
+ void doSomeSweeping(boolean tryHarder) {
+ // assert lock.getHoldCount() == 1;
+ // assert head != null;
+ int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES;
+ Node o, p;
+ final Node sweeper = this.sweeper;
+ boolean passedGo; // to limit search to one full sweep
+
+ if (sweeper == null) {
+ o = null;
+ p = head;
+ passedGo = true;
+ } else {
+ o = sweeper;
+ p = o.next;
+ passedGo = false;
+ }
+
+ for (; probes > 0; probes--) {
+ if (p == null) {
+ if (passedGo)
+ break;
+ o = null;
+ p = head;
+ passedGo = true;
}
- else
- lastItem = x;
- while (--remaining > 0 && // skip over nulls
- (nextItem = itemAt(nextIndex = inc(nextIndex))) == null)
- ;
- return x;
- } finally {
- lock.unlock();
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.isDetached()) {
+ // found a discarded/exhausted iterator
+ probes = LONG_SWEEP_PROBES; // "try harder"
+ // unlink p
+ p.clear();
+ p.next = null;
+ if (o == null) {
+ head = next;
+ if (next == null) {
+ // We've run out of iterators to track; retire
+ itrs = null;
+ return;
+ }
+ }
+ else
+ o.next = next;
+ } else {
+ o = p;
+ }
+ p = next;
}
+
+ this.sweeper = (p == null) ? null : o;
}
- public void remove() {
- final ReentrantLock lock = ArrayBlockingQueue.this.lock;
- lock.lock();
- try {
- int i = lastRet;
- if (i == -1)
- throw new IllegalStateException();
- lastRet = -1;
- E x = lastItem;
- lastItem = null;
- // only remove if item still at index
- if (x != null && x == items[i]) {
- boolean removingHead = (i == takeIndex);
- removeAt(i);
- if (!removingHead)
- nextIndex = dec(nextIndex);
+ /**
+ * Adds a new iterator to the linked list of tracked iterators.
+ */
+ void register(Itr itr) {
+ // assert lock.getHoldCount() == 1;
+ head = new Node(itr, head);
+ }
+
+ /**
+ * Called whenever takeIndex wraps around to 0.
+ *
+ * Notifies all iterators, and expunges any that are now stale.
+ */
+ void takeIndexWrapped() {
+ // assert lock.getHoldCount() == 1;
+ cycles++;
+ for (Node o = null, p = head; p != null;) {
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.takeIndexWrapped()) {
+ // unlink p
+ // assert it == null || it.isDetached();
+ p.clear();
+ p.next = null;
+ if (o == null)
+ head = next;
+ else
+ o.next = next;
+ } else {
+ o = p;
}
- } finally {
- lock.unlock();
+ p = next;
}
+ if (head == null) // no more iterators to track
+ itrs = null;
+ }
+
+ /**
+ * Called whenever an interior remove (not at takeIndex) occured.
+ *
+ * Notifies all iterators, and expunges any that are now stale.
+ */
+ void removedAt(int removedIndex) {
+ for (Node o = null, p = head; p != null;) {
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.removedAt(removedIndex)) {
+ // unlink p
+ // assert it == null || it.isDetached();
+ p.clear();
+ p.next = null;
+ if (o == null)
+ head = next;
+ else
+ o.next = next;
+ } else {
+ o = p;
+ }
+ p = next;
+ }
+ if (head == null) // no more iterators to track
+ itrs = null;
+ }
+
+ /**
+ * Called whenever the queue becomes empty.
+ *
+ * Notifies all active iterators that the queue is empty,
+ * clears all weak refs, and unlinks the itrs datastructure.
+ */
+ void queueIsEmpty() {
+ // assert lock.getHoldCount() == 1;
+ for (Node p = head; p != null; p = p.next) {
+ Itr it = p.get();
+ if (it != null) {
+ p.clear();
+ it.shutdown();
+ }
+ }
+ head = null;
+ itrs = null;
+ }
+
+ /**
+ * Called whenever an element has been dequeued (at takeIndex).
+ */
+ void elementDequeued() {
+ // assert lock.getHoldCount() == 1;
+ if (count == 0)
+ queueIsEmpty();
+ else if (takeIndex == 0)
+ takeIndexWrapped();
}
}
+ /**
+ * Iterator for ArrayBlockingQueue.
+ *
+ * To maintain weak consistency with respect to puts and takes, we
+ * read ahead one slot, so as to not report hasNext true but then
+ * not have an element to return.
+ *
+ * We switch into "detached" mode (allowing prompt unlinking from
+ * itrs without help from the GC) when all indices are negative, or
+ * when hasNext returns false for the first time. This allows the
+ * iterator to track concurrent updates completely accurately,
+ * except for the corner case of the user calling Iterator.remove()
+ * after hasNext() returned false. Even in this case, we ensure
+ * that we don't remove the wrong element by keeping track of the
+ * expected element to remove, in lastItem. Yes, we may fail to
+ * remove lastItem from the queue if it moved due to an interleaved
+ * interior remove while in detached mode.
+ */
+ private class Itr implements Iterator<E> {
+ /** Index to look for new nextItem; NONE at end */
+ private int cursor;
+
+ /** Element to be returned by next call to next(); null if none */
+ private E nextItem;
+
+ /** Index of nextItem; NONE if none, REMOVED if removed elsewhere */
+ private int nextIndex;
+
+ /** Last element returned; null if none or not detached. */
+ private E lastItem;
+
+ /** Index of lastItem, NONE if none, REMOVED if removed elsewhere */
+ private int lastRet;
+
+ /** Previous value of takeIndex, or DETACHED when detached */
+ private int prevTakeIndex;
+
+ /** Previous value of iters.cycles */
+ private int prevCycles;
+
+ /** Special index value indicating "not available" or "undefined" */
+ private static final int NONE = -1;
+
+ /**
+ * Special index value indicating "removed elsewhere", that is,
+ * removed by some operation other than a call to this.remove().
+ */
+ private static final int REMOVED = -2;
+
+ /** Special value for prevTakeIndex indicating "detached mode" */
+ private static final int DETACHED = -3;
+
+ Itr() {
+ // assert lock.getHoldCount() == 0;
+ lastRet = NONE;
+ final ReentrantLock lock = ArrayBlockingQueue.this.lock;
+ lock.lock();
+ try {
+ if (count == 0) {
+ // assert itrs == null;
+ cursor = NONE;
+ nextIndex = NONE;
+ prevTakeIndex = DETACHED;
+ } else {
+ final int takeIndex = ArrayBlockingQueue.this.takeIndex;
+ prevTakeIndex = takeIndex;
+ nextItem = itemAt(nextIndex = takeIndex);
+ cursor = incCursor(takeIndex);
+ if (itrs == null) {
+ itrs = new Itrs(this);
+ } else {
+ itrs.register(this); // in this order
+ itrs.doSomeSweeping(false);
+ }
+ prevCycles = itrs.cycles;
+ // assert takeIndex >= 0;
+ // assert prevTakeIndex == takeIndex;
+ // assert nextIndex >= 0;
+ // assert nextItem != null;
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ boolean isDetached() {
+ // assert lock.getHoldCount() == 1;
+ return prevTakeIndex < 0;
+ }
+
+ private int incCursor(int index) {
+ // assert lock.getHoldCount() == 1;
+ index = inc(index);
+ if (index == putIndex)
+ index = NONE;
+ return index;
+ }
+
+ /**
+ * Returns true if index is invalidated by the given number of
+ * dequeues, starting from prevTakeIndex.
+ */
+ private boolean invalidated(int index, int prevTakeIndex,
+ long dequeues, int length) {
+ if (index < 0)
+ return false;
+ int distance = index - prevTakeIndex;
+ if (distance < 0)
+ distance += length;
+ return dequeues > distance;
+ }
+
+ /**
+ * Adjusts indices to incorporate all dequeues since the last
+ * operation on this iterator. Call only from iterating thread.
+ */
+ private void incorporateDequeues() {
+ // assert lock.getHoldCount() == 1;
+ // assert itrs != null;
+ // assert !isDetached();
+ // assert count > 0;
+
+ final int cycles = itrs.cycles;
+ final int takeIndex = ArrayBlockingQueue.this.takeIndex;
+ final int prevCycles = this.prevCycles;
+ final int prevTakeIndex = this.prevTakeIndex;
+
+ if (cycles != prevCycles || takeIndex != prevTakeIndex) {
+ final int len = items.length;
+ // how far takeIndex has advanced since the previous
+ // operation of this iterator
+ long dequeues = (cycles - prevCycles) * len
+ + (takeIndex - prevTakeIndex);
+
+ // Check indices for invalidation
+ if (invalidated(lastRet, prevTakeIndex, dequeues, len))
+ lastRet = REMOVED;
+ if (invalidated(nextIndex, prevTakeIndex, dequeues, len))
+ nextIndex = REMOVED;
+ if (invalidated(cursor, prevTakeIndex, dequeues, len))
+ cursor = takeIndex;
+
+ if (cursor < 0 && nextIndex < 0 && lastRet < 0)
+ detach();
+ else {
+ this.prevCycles = cycles;
+ this.prevTakeIndex = takeIndex;
+ }
+ }
+ }
+
+ /**
+ * Called when itrs should stop tracking this iterator, either
+ * because there are no more indices to update (cursor < 0 &&
+ * nextIndex < 0 && lastRet < 0) or as a special exception, when
+ * lastRet >= 0, because hasNext() is about to return false for the
+ * first time. Call only from iterating thread.
+ */
+ private void detach() {
+ // Switch to detached mode
+ // assert lock.getHoldCount() == 1;
+ // assert cursor == NONE;
+ // assert nextIndex < 0;
+ // assert lastRet < 0 || nextItem == null;
+ // assert lastRet < 0 ^ lastItem != null;
+ if (prevTakeIndex >= 0) {
+ // assert itrs != null;
+ prevTakeIndex = DETACHED;
+ // try to unlink from itrs (but not too hard)
+ itrs.doSomeSweeping(true);
+ }
+ }
+
+ /**
+ * For performance reasons, we would like not to acquire a lock in
+ * hasNext in the common case. To allow for this, we only access
+ * fields (i.e. nextItem) that are not modified by update operations
+ * triggered by queue modifications.
+ */
+ public boolean hasNext() {
+ // assert lock.getHoldCount() == 0;
+ if (nextItem != null)
+ return true;
+ noNext();
+ return false;
+ }
+
+ private void noNext() {
+ final ReentrantLock lock = ArrayBlockingQueue.this.lock;
+ lock.lock();
+ try {
+ // assert cursor == NONE;
+ // assert nextIndex == NONE;
+ if (!isDetached()) {
+ // assert lastRet >= 0;
+ incorporateDequeues(); // might update lastRet
+ if (lastRet >= 0) {
+ lastItem = itemAt(lastRet);
+ // assert lastItem != null;
+ detach();
+ }
+ }
+ // assert isDetached();
+ // assert lastRet < 0 ^ lastItem != null;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public E next() {
+ // assert lock.getHoldCount() == 0;
+ final E x = nextItem;
+ if (x == null)
+ throw new NoSuchElementException();
+ final ReentrantLock lock = ArrayBlockingQueue.this.lock;
+ lock.lock();
+ try {
+ if (!isDetached())
+ incorporateDequeues();
+ // assert nextIndex != NONE;
+ // assert lastItem == null;
+ lastRet = nextIndex;
+ final int cursor = this.cursor;
+ if (cursor >= 0) {
+ nextItem = itemAt(nextIndex = cursor);
+ // assert nextItem != null;
+ this.cursor = incCursor(cursor);
+ } else {
+ nextIndex = NONE;
+ nextItem = null;
+ }
+ } finally {
+ lock.unlock();
+ }
+ return x;
+ }
+
+ public void remove() {
+ // assert lock.getHoldCount() == 0;
+ final ReentrantLock lock = ArrayBlockingQueue.this.lock;
+ lock.lock();
+ try {
+ if (!isDetached())
+ incorporateDequeues(); // might update lastRet or detach
+ final int lastRet = this.lastRet;
+ this.lastRet = NONE;
+ if (lastRet >= 0) {
+ if (!isDetached())
+ removeAt(lastRet);
+ else {
+ final E lastItem = this.lastItem;
+ // assert lastItem != null;
+ this.lastItem = null;
+ if (itemAt(lastRet) == lastItem)
+ removeAt(lastRet);
+ }
+ } else if (lastRet == NONE)
+ throw new IllegalStateException();
+ // else lastRet == REMOVED and the last returned element was
+ // previously asynchronously removed via an operation other
+ // than this.remove(), so nothing to do.
+
+ if (cursor < 0 && nextIndex < 0)
+ detach();
+ } finally {
+ lock.unlock();
+ // assert lastRet == NONE;
+ // assert lastItem == null;
+ }
+ }
+
+ /**
+ * Called to notify the iterator that the queue is empty, or that it
+ * has fallen hopelessly behind, so that it should abandon any
+ * further iteration, except possibly to return one more element
+ * from next(), as promised by returning true from hasNext().
+ */
+ void shutdown() {
+ // assert lock.getHoldCount() == 1;
+ cursor = NONE;
+ if (nextIndex >= 0)
+ nextIndex = REMOVED;
+ if (lastRet >= 0) {
+ lastRet = REMOVED;
+ lastItem = null;
+ }
+ prevTakeIndex = DETACHED;
+ // Don't set nextItem to null because we must continue to be
+ // able to return it on next().
+ //
+ // Caller will unlink from itrs when convenient.
+ }
+
+ private int distance(int index, int prevTakeIndex, int length) {
+ int distance = index - prevTakeIndex;
+ if (distance < 0)
+ distance += length;
+ return distance;
+ }
+
+ /**
+ * Called whenever an interior remove (not at takeIndex) occured.
+ *
+ * @return true if this iterator should be unlinked from itrs
+ */
+ boolean removedAt(int removedIndex) {
+ // assert lock.getHoldCount() == 1;
+ if (isDetached())
+ return true;
+
+ final int cycles = itrs.cycles;
+ final int takeIndex = ArrayBlockingQueue.this.takeIndex;
+ final int prevCycles = this.prevCycles;
+ final int prevTakeIndex = this.prevTakeIndex;
+ final int len = items.length;
+ int cycleDiff = cycles - prevCycles;
+ if (removedIndex < takeIndex)
+ cycleDiff++;
+ final int removedDistance =
+ (cycleDiff * len) + (removedIndex - prevTakeIndex);
+ // assert removedDistance >= 0;
+ int cursor = this.cursor;
+ if (cursor >= 0) {
+ int x = distance(cursor, prevTakeIndex, len);
+ if (x == removedDistance) {
+ if (cursor == putIndex)
+ this.cursor = cursor = NONE;
+ }
+ else if (x > removedDistance) {
+ // assert cursor != prevTakeIndex;
+ this.cursor = cursor = dec(cursor);
+ }
+ }
+ int lastRet = this.lastRet;
+ if (lastRet >= 0) {
+ int x = distance(lastRet, prevTakeIndex, len);
+ if (x == removedDistance)
+ this.lastRet = lastRet = REMOVED;
+ else if (x > removedDistance)
+ this.lastRet = lastRet = dec(lastRet);
+ }
+ int nextIndex = this.nextIndex;
+ if (nextIndex >= 0) {
+ int x = distance(nextIndex, prevTakeIndex, len);
+ if (x == removedDistance)
+ this.nextIndex = nextIndex = REMOVED;
+ else if (x > removedDistance)
+ this.nextIndex = nextIndex = dec(nextIndex);
+ }
+ else if (cursor < 0 && nextIndex < 0 && lastRet < 0) {
+ this.prevTakeIndex = DETACHED;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Called whenever takeIndex wraps around to zero.
+ *
+ * @return true if this iterator should be unlinked from itrs
+ */
+ boolean takeIndexWrapped() {
+ // assert lock.getHoldCount() == 1;
+ if (isDetached())
+ return true;
+ if (itrs.cycles - prevCycles > 1) {
+ // All the elements that existed at the time of the last
+ // operation are gone, so abandon further iteration.
+ shutdown();
+ return true;
+ }
+ return false;
+ }
+
+// /** Uncomment for debugging. */
+// public String toString() {
+// return ("cursor=" + cursor + " " +
+// "nextIndex=" + nextIndex + " " +
+// "lastRet=" + lastRet + " " +
+// "nextItem=" + nextItem + " " +
+// "lastItem=" + lastItem + " " +
+// "prevCycles=" + prevCycles + " " +
+// "prevTakeIndex=" + prevTakeIndex + " " +
+// "size()=" + size() + " " +
+// "remainingCapacity()=" + remainingCapacity());
+// }
+ }
}
diff --git a/luni/src/main/java/java/util/concurrent/BlockingDeque.java b/luni/src/main/java/java/util/concurrent/BlockingDeque.java
index 136df9c..34f103d 100644
--- a/luni/src/main/java/java/util/concurrent/BlockingDeque.java
+++ b/luni/src/main/java/java/util/concurrent/BlockingDeque.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -36,9 +36,9 @@
* <tr>
* <td><b>Insert</b></td>
* <td>{@link #addFirst addFirst(e)}</td>
- * <td>{@link #offerFirst offerFirst(e)}</td>
+ * <td>{@link #offerFirst(Object) offerFirst(e)}</td>
* <td>{@link #putFirst putFirst(e)}</td>
- * <td>{@link #offerFirst offerFirst(e, time, unit)}</td>
+ * <td>{@link #offerFirst(Object, long, TimeUnit) offerFirst(e, time, unit)}</td>
* </tr>
* <tr>
* <td><b>Remove</b></td>
@@ -67,9 +67,9 @@
* <tr>
* <td><b>Insert</b></td>
* <td>{@link #addLast addLast(e)}</td>
- * <td>{@link #offerLast offerLast(e)}</td>
+ * <td>{@link #offerLast(Object) offerLast(e)}</td>
* <td>{@link #putLast putLast(e)}</td>
- * <td>{@link #offerLast offerLast(e, time, unit)}</td>
+ * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td>
* </tr>
* <tr>
* <td><b>Remove</b></td>
@@ -106,20 +106,20 @@
* <td ALIGN=CENTER COLSPAN = 2> <b>Insert</b></td>
* </tr>
* <tr>
- * <td>{@link #add add(e)}</td>
- * <td>{@link #addLast addLast(e)}</td>
+ * <td>{@link #add(Object) add(e)}</td>
+ * <td>{@link #addLast(Object) addLast(e)}</td>
* </tr>
* <tr>
- * <td>{@link #offer offer(e)}</td>
- * <td>{@link #offerLast offerLast(e)}</td>
+ * <td>{@link #offer(Object) offer(e)}</td>
+ * <td>{@link #offerLast(Object) offerLast(e)}</td>
* </tr>
* <tr>
- * <td>{@link #put put(e)}</td>
- * <td>{@link #putLast putLast(e)}</td>
+ * <td>{@link #put(Object) put(e)}</td>
+ * <td>{@link #putLast(Object) putLast(e)}</td>
* </tr>
* <tr>
- * <td>{@link #offer offer(e, time, unit)}</td>
- * <td>{@link #offerLast offerLast(e, time, unit)}</td>
+ * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td>
+ * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td>
* </tr>
* <tr>
* <td ALIGN=CENTER COLSPAN = 2> <b>Remove</b></td>
@@ -181,7 +181,7 @@
* possible to do so immediately without violating capacity restrictions,
* throwing an <tt>IllegalStateException</tt> if no space is currently
* available. When using a capacity-restricted deque, it is generally
- * preferable to use {@link #offerFirst offerFirst}.
+ * preferable to use {@link #offerFirst(Object) offerFirst}.
*
* @param e the element to add
* @throws IllegalStateException {@inheritDoc}
@@ -196,7 +196,7 @@
* possible to do so immediately without violating capacity restrictions,
* throwing an <tt>IllegalStateException</tt> if no space is currently
* available. When using a capacity-restricted deque, it is generally
- * preferable to use {@link #offerLast offerLast}.
+ * preferable to use {@link #offerLast(Object) offerLast}.
*
* @param e the element to add
* @throws IllegalStateException {@inheritDoc}
@@ -212,7 +212,7 @@
* returning <tt>true</tt> upon success and <tt>false</tt> if no space is
* currently available.
* When using a capacity-restricted deque, this method is generally
- * preferable to the {@link #addFirst addFirst} method, which can
+ * preferable to the {@link #addFirst(Object) addFirst} method, which can
* fail to insert an element only by throwing an exception.
*
* @param e the element to add
@@ -228,7 +228,7 @@
* returning <tt>true</tt> upon success and <tt>false</tt> if no space is
* currently available.
* When using a capacity-restricted deque, this method is generally
- * preferable to the {@link #addLast addLast} method, which can
+ * preferable to the {@link #addLast(Object) addLast} method, which can
* fail to insert an element only by throwing an exception.
*
* @param e the element to add
@@ -371,8 +371,10 @@
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this deque
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeFirstOccurrence(Object o);
@@ -387,8 +389,10 @@
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this deque
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeLastOccurrence(Object o);
@@ -401,9 +405,9 @@
* <tt>true</tt> upon success and throwing an
* <tt>IllegalStateException</tt> if no space is currently available.
* When using a capacity-restricted deque, it is generally preferable to
- * use {@link #offer offer}.
+ * use {@link #offer(Object) offer}.
*
- * <p>This method is equivalent to {@link #addLast addLast}.
+ * <p>This method is equivalent to {@link #addLast(Object) addLast}.
*
* @param e the element to add
* @throws IllegalStateException {@inheritDoc}
@@ -424,7 +428,7 @@
* generally preferable to the {@link #add} method, which can fail to
* insert an element only by throwing an exception.
*
- * <p>This method is equivalent to {@link #offerLast offerLast}.
+ * <p>This method is equivalent to {@link #offerLast(Object) offerLast}.
*
* @param e the element to add
* @throws ClassCastException if the class of the specified element
@@ -440,7 +444,7 @@
* (in other words, at the tail of this deque), waiting if necessary for
* space to become available.
*
- * <p>This method is equivalent to {@link #putLast putLast}.
+ * <p>This method is equivalent to {@link #putLast(Object) putLast}.
*
* @param e the element to add
* @throws InterruptedException {@inheritDoc}
@@ -458,7 +462,7 @@
* specified wait time if necessary for space to become available.
*
* <p>This method is equivalent to
- * {@link #offerLast offerLast}.
+ * {@link #offerLast(Object,long,TimeUnit) offerLast}.
*
* @param e the element to add
* @return <tt>true</tt> if the element was added to this deque, else
@@ -557,13 +561,15 @@
* (or equivalently, if this deque changed as a result of the call).
*
* <p>This method is equivalent to
- * {@link #removeFirstOccurrence removeFirstOccurrence}.
+ * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
*
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> if this deque changed as a result of the call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this deque
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
@@ -575,8 +581,10 @@
* @param o object to be checked for containment in this deque
* @return <tt>true</tt> if this deque contains the specified element
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this deque
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
public boolean contains(Object o);
@@ -602,7 +610,7 @@
* words, inserts the element at the front of this deque unless it would
* violate capacity restrictions.
*
- * <p>This method is equivalent to {@link #addFirst addFirst}.
+ * <p>This method is equivalent to {@link #addFirst(Object) addFirst}.
*
* @throws IllegalStateException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
diff --git a/luni/src/main/java/java/util/concurrent/BlockingQueue.java b/luni/src/main/java/java/util/concurrent/BlockingQueue.java
index d01c097..6cfe52b 100644
--- a/luni/src/main/java/java/util/concurrent/BlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/BlockingQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -42,7 +42,7 @@
* <td>{@link #add add(e)}</td>
* <td>{@link #offer offer(e)}</td>
* <td>{@link #put put(e)}</td>
- * <td>{@link #offer offer(e, time, unit)}</td>
+ * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td>
* </tr>
* <tr>
* <td><b>Remove</b></td>
@@ -102,7 +102,7 @@
* Usage example, based on a typical producer-consumer scenario.
* Note that a <tt>BlockingQueue</tt> can safely be used with multiple
* producers and multiple consumers.
- * <pre>
+ * <pre> {@code
* class Producer implements Runnable {
* private final BlockingQueue queue;
* Producer(BlockingQueue q) { queue = q; }
@@ -135,8 +135,7 @@
* new Thread(c1).start();
* new Thread(c2).start();
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>Memory consistency effects: As with other concurrent
* collections, actions in a thread prior to placing an object into a
@@ -156,7 +155,7 @@
* <tt>true</tt> upon success and throwing an
* <tt>IllegalStateException</tt> if no space is currently available.
* When using a capacity-restricted queue, it is generally preferable to
- * use {@link #offer offer}.
+ * use {@link #offer(Object) offer}.
*
* @param e the element to add
* @return <tt>true</tt> (as specified by {@link Collection#add})
@@ -274,8 +273,10 @@
* @param o element to be removed from this queue, if present
* @return <tt>true</tt> if this queue changed as a result of the call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this queue (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this queue
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
@@ -287,8 +288,10 @@
* @param o object to be checked for containment in this queue
* @return <tt>true</tt> if this queue contains the specified element
* @throws ClassCastException if the class of the specified element
- * is incompatible with this queue (optional)
- * @throws NullPointerException if the specified element is null (optional)
+ * is incompatible with this queue
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified element is null
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
public boolean contains(Object o);
diff --git a/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java b/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java
index 3f93fbb..76fae13 100644
--- a/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java
+++ b/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/Callable.java b/luni/src/main/java/java/util/concurrent/Callable.java
index abc4d04..293544b 100644
--- a/luni/src/main/java/java/util/concurrent/Callable.java
+++ b/luni/src/main/java/java/util/concurrent/Callable.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/CancellationException.java b/luni/src/main/java/java/util/concurrent/CancellationException.java
index 2c29544..dc452e4 100644
--- a/luni/src/main/java/java/util/concurrent/CancellationException.java
+++ b/luni/src/main/java/java/util/concurrent/CancellationException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/CompletionService.java b/luni/src/main/java/java/util/concurrent/CompletionService.java
index df9f719..7b0931c 100644
--- a/luni/src/main/java/java/util/concurrent/CompletionService.java
+++ b/luni/src/main/java/java/util/concurrent/CompletionService.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
index c142e2a..c85a5cc 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
@@ -1,16 +1,13 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.concurrent.locks.*;
import java.util.*;
import java.io.Serializable;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
// BEGIN android-note
// removed link to collections framework docs
@@ -76,7 +73,25 @@
/*
* The basic strategy is to subdivide the table among Segments,
- * each of which itself is a concurrently readable hash table.
+ * each of which itself is a concurrently readable hash table. To
+ * reduce footprint, all but one segments are constructed only
+ * when first needed (see ensureSegment). To maintain visibility
+ * in the presence of lazy construction, accesses to segments as
+ * well as elements of segment's table must use volatile access,
+ * which is done via Unsafe within methods segmentAt etc
+ * below. These provide the functionality of AtomicReferenceArrays
+ * but reduce the levels of indirection. Additionally,
+ * volatile-writes of table elements and entry "next" fields
+ * within locked operations use the cheaper "lazySet" forms of
+ * writes (via putOrderedObject) because these writes are always
+ * followed by lock releases that maintain sequential consistency
+ * of table updates.
+ *
+ * Historical note: The previous version of this class relied
+ * heavily on "final" fields, which avoided some volatile reads at
+ * the expense of a large initial footprint. Some remnants of
+ * that design (including forced construction of segment 0) exist
+ * to ensure serialization compatibility.
*/
/* ---------------- Constants -------------- */
@@ -108,8 +123,15 @@
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
+ * The minimum capacity for per-segment tables. Must be a power
+ * of two, at least two to avoid immediate resizing on next use
+ * after lazy construction.
+ */
+ static final int MIN_SEGMENT_TABLE_CAPACITY = 2;
+
+ /**
* The maximum number of segments to allow; used to bound
- * constructor arguments.
+ * constructor arguments. Must be power of two less than 1 << 24.
*/
static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
@@ -135,7 +157,7 @@
final int segmentShift;
/**
- * The segments, each of which is a specialized hash table
+ * The segments, each of which is a specialized hash table.
*/
final Segment<K,V>[] segments;
@@ -143,7 +165,66 @@
transient Set<Map.Entry<K,V>> entrySet;
transient Collection<V> values;
- /* ---------------- Small Utilities -------------- */
+ /**
+ * ConcurrentHashMap list entry. Note that this is never exported
+ * out as a user-visible Map.Entry.
+ */
+ static final class HashEntry<K,V> {
+ final int hash;
+ final K key;
+ volatile V value;
+ volatile HashEntry<K,V> next;
+
+ HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
+ this.hash = hash;
+ this.key = key;
+ this.value = value;
+ this.next = next;
+ }
+
+ /**
+ * Sets next field with volatile write semantics. (See above
+ * about use of putOrderedObject.)
+ */
+ final void setNext(HashEntry<K,V> n) {
+ UNSAFE.putOrderedObject(this, nextOffset, n);
+ }
+
+ // Unsafe mechanics
+ static final sun.misc.Unsafe UNSAFE;
+ static final long nextOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = HashEntry.class;
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+ }
+
+ /**
+ * Gets the ith element of given table (if nonnull) with volatile
+ * read semantics. Note: This is manually integrated into a few
+ * performance-sensitive methods to reduce call overhead.
+ */
+ @SuppressWarnings("unchecked")
+ static final <K,V> HashEntry<K,V> entryAt(HashEntry<K,V>[] tab, int i) {
+ return (tab == null) ? null :
+ (HashEntry<K,V>) UNSAFE.getObjectVolatile
+ (tab, ((long)i << TSHIFT) + TBASE);
+ }
+
+ /**
+ * Sets the ith element of given table, with volatile write
+ * semantics. (See above about use of putOrderedObject.)
+ */
+ static final <K,V> void setEntryAt(HashEntry<K,V>[] tab, int i,
+ HashEntry<K,V> e) {
+ UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e);
+ }
/**
* Applies a supplemental hash function to a given hashCode, which
@@ -164,104 +245,67 @@
}
/**
- * Returns the segment that should be used for key with given hash
- * @param hash the hash code for the key
- * @return the segment
- */
- final Segment<K,V> segmentFor(int hash) {
- return segments[(hash >>> segmentShift) & segmentMask];
- }
-
- /* ---------------- Inner Classes -------------- */
-
- /**
- * ConcurrentHashMap list entry. Note that this is never exported
- * out as a user-visible Map.Entry.
- *
- * Because the value field is volatile, not final, it is legal wrt
- * the Java Memory Model for an unsynchronized reader to see null
- * instead of initial value when read via a data race. Although a
- * reordering leading to this is not likely to ever actually
- * occur, the Segment.readValueUnderLock method is used as a
- * backup in case a null (pre-initialized) value is ever seen in
- * an unsynchronized access method.
- */
- static final class HashEntry<K,V> {
- final K key;
- final int hash;
- volatile V value;
- final HashEntry<K,V> next;
-
- HashEntry(K key, int hash, HashEntry<K,V> next, V value) {
- this.key = key;
- this.hash = hash;
- this.next = next;
- this.value = value;
- }
-
- @SuppressWarnings("unchecked")
- static final <K,V> HashEntry<K,V>[] newArray(int i) {
- return new HashEntry[i];
- }
- }
-
- /**
* Segments are specialized versions of hash tables. This
* subclasses from ReentrantLock opportunistically, just to
* simplify some locking and avoid separate construction.
*/
static final class Segment<K,V> extends ReentrantLock implements Serializable {
/*
- * Segments maintain a table of entry lists that are ALWAYS
- * kept in a consistent state, so can be read without locking.
- * Next fields of nodes are immutable (final). All list
- * additions are performed at the front of each bin. This
- * makes it easy to check changes, and also fast to traverse.
- * When nodes would otherwise be changed, new nodes are
- * created to replace them. This works well for hash tables
- * since the bin lists tend to be short. (The average length
- * is less than two for the default load factor threshold.)
+ * Segments maintain a table of entry lists that are always
+ * kept in a consistent state, so can be read (via volatile
+ * reads of segments and tables) without locking. This
+ * requires replicating nodes when necessary during table
+ * resizing, so the old lists can be traversed by readers
+ * still using old version of table.
*
- * Read operations can thus proceed without locking, but rely
- * on selected uses of volatiles to ensure that completed
- * write operations performed by other threads are
- * noticed. For most purposes, the "count" field, tracking the
- * number of elements, serves as that volatile variable
- * ensuring visibility. This is convenient because this field
- * needs to be read in many read operations anyway:
- *
- * - All (unsynchronized) read operations must first read the
- * "count" field, and should not look at table entries if
- * it is 0.
- *
- * - All (synchronized) write operations should write to
- * the "count" field after structurally changing any bin.
- * The operations must not take any action that could even
- * momentarily cause a concurrent read operation to see
- * inconsistent data. This is made easier by the nature of
- * the read operations in Map. For example, no operation
- * can reveal that the table has grown but the threshold
- * has not yet been updated, so there are no atomicity
- * requirements for this with respect to reads.
- *
- * As a guide, all critical volatile reads and writes to the
- * count field are marked in code comments.
+ * This class defines only mutative methods requiring locking.
+ * Except as noted, the methods of this class perform the
+ * per-segment versions of ConcurrentHashMap methods. (Other
+ * methods are integrated directly into ConcurrentHashMap
+ * methods.) These mutative methods use a form of controlled
+ * spinning on contention via methods scanAndLock and
+ * scanAndLockForPut. These intersperse tryLocks with
+ * traversals to locate nodes. The main benefit is to absorb
+ * cache misses (which are very common for hash tables) while
+ * obtaining locks so that traversal is faster once
+ * acquired. We do not actually use the found nodes since they
+ * must be re-acquired under lock anyway to ensure sequential
+ * consistency of updates (and in any case may be undetectably
+ * stale), but they will normally be much faster to re-locate.
+ * Also, scanAndLockForPut speculatively creates a fresh node
+ * to use in put if no node is found.
*/
private static final long serialVersionUID = 2249069246763182397L;
/**
- * The number of elements in this segment's region.
+ * The maximum number of times to tryLock in a prescan before
+ * possibly blocking on acquire in preparation for a locked
+ * segment operation. On multiprocessors, using a bounded
+ * number of retries maintains cache acquired while locating
+ * nodes.
*/
- transient volatile int count;
+ static final int MAX_SCAN_RETRIES =
+ Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
/**
- * Number of updates that alter the size of the table. This is
- * used during bulk-read methods to make sure they see a
- * consistent snapshot: If modCounts change during a traversal
- * of segments computing size or checking containsValue, then
- * we might have an inconsistent view of state so (usually)
- * must retry.
+ * The per-segment table. Elements are accessed via
+ * entryAt/setEntryAt providing volatile semantics.
+ */
+ transient volatile HashEntry<K,V>[] table;
+
+ /**
+ * The number of elements. Accessed only either within locks
+ * or among other volatile reads that maintain visibility.
+ */
+ transient int count;
+
+ /**
+ * The total number of mutative operations in this segment.
+ * Even though this may overflows 32 bits, it provides
+ * sufficient accuracy for stability checks in CHM isEmpty()
+ * and size() methods. Accessed only either within locks or
+ * among other volatile reads that maintain visibility.
*/
transient int modCount;
@@ -273,11 +317,6 @@
transient int threshold;
/**
- * The per-segment table.
- */
- transient volatile HashEntry<K,V>[] table;
-
- /**
* The load factor for the hash table. Even though this value
* is same for all segments, it is replicated to avoid needing
* links to outer object.
@@ -285,202 +324,93 @@
*/
final float loadFactor;
- Segment(int initialCapacity, float lf) {
- loadFactor = lf;
- setTable(HashEntry.<K,V>newArray(initialCapacity));
+ Segment(float lf, int threshold, HashEntry<K,V>[] tab) {
+ this.loadFactor = lf;
+ this.threshold = threshold;
+ this.table = tab;
}
+ final V put(K key, int hash, V value, boolean onlyIfAbsent) {
+ HashEntry<K,V> node = tryLock() ? null :
+ scanAndLockForPut(key, hash, value);
+ V oldValue;
+ try {
+ HashEntry<K,V>[] tab = table;
+ int index = (tab.length - 1) & hash;
+ HashEntry<K,V> first = entryAt(tab, index);
+ for (HashEntry<K,V> e = first;;) {
+ if (e != null) {
+ K k;
+ if ((k = e.key) == key ||
+ (e.hash == hash && key.equals(k))) {
+ oldValue = e.value;
+ if (!onlyIfAbsent) {
+ e.value = value;
+ ++modCount;
+ }
+ break;
+ }
+ e = e.next;
+ }
+ else {
+ if (node != null)
+ node.setNext(first);
+ else
+ node = new HashEntry<K,V>(hash, key, value, first);
+ int c = count + 1;
+ if (c > threshold && tab.length < MAXIMUM_CAPACITY)
+ rehash(node);
+ else
+ setEntryAt(tab, index, node);
+ ++modCount;
+ count = c;
+ oldValue = null;
+ break;
+ }
+ }
+ } finally {
+ unlock();
+ }
+ return oldValue;
+ }
+
+ /**
+ * Doubles size of table and repacks entries, also adding the
+ * given node to new table
+ */
@SuppressWarnings("unchecked")
- static final <K,V> Segment<K,V>[] newArray(int i) {
- return new Segment[i];
- }
-
- /**
- * Sets table to new HashEntry array.
- * Call only while holding lock or in constructor.
- */
- void setTable(HashEntry<K,V>[] newTable) {
- threshold = (int)(newTable.length * loadFactor);
- table = newTable;
- }
-
- /**
- * Returns properly casted first entry of bin for given hash.
- */
- HashEntry<K,V> getFirst(int hash) {
- HashEntry<K,V>[] tab = table;
- return tab[hash & (tab.length - 1)];
- }
-
- /**
- * Reads value field of an entry under lock. Called if value
- * field ever appears to be null. This is possible only if a
- * compiler happens to reorder a HashEntry initialization with
- * its table assignment, which is legal under memory model
- * but is not known to ever occur.
- */
- V readValueUnderLock(HashEntry<K,V> e) {
- lock();
- try {
- return e.value;
- } finally {
- unlock();
- }
- }
-
- /* Specialized implementations of map methods */
-
- V get(Object key, int hash) {
- if (count != 0) { // read-volatile
- HashEntry<K,V> e = getFirst(hash);
- while (e != null) {
- if (e.hash == hash && key.equals(e.key)) {
- V v = e.value;
- if (v != null)
- return v;
- return readValueUnderLock(e); // recheck
- }
- e = e.next;
- }
- }
- return null;
- }
-
- boolean containsKey(Object key, int hash) {
- if (count != 0) { // read-volatile
- HashEntry<K,V> e = getFirst(hash);
- while (e != null) {
- if (e.hash == hash && key.equals(e.key))
- return true;
- e = e.next;
- }
- }
- return false;
- }
-
- boolean containsValue(Object value) {
- if (count != 0) { // read-volatile
- HashEntry<K,V>[] tab = table;
- int len = tab.length;
- for (int i = 0 ; i < len; i++) {
- for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
- V v = e.value;
- if (v == null) // recheck
- v = readValueUnderLock(e);
- if (value.equals(v))
- return true;
- }
- }
- }
- return false;
- }
-
- boolean replace(K key, int hash, V oldValue, V newValue) {
- lock();
- try {
- HashEntry<K,V> e = getFirst(hash);
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
-
- boolean replaced = false;
- if (e != null && oldValue.equals(e.value)) {
- replaced = true;
- e.value = newValue;
- }
- return replaced;
- } finally {
- unlock();
- }
- }
-
- V replace(K key, int hash, V newValue) {
- lock();
- try {
- HashEntry<K,V> e = getFirst(hash);
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
-
- V oldValue = null;
- if (e != null) {
- oldValue = e.value;
- e.value = newValue;
- }
- return oldValue;
- } finally {
- unlock();
- }
- }
-
-
- V put(K key, int hash, V value, boolean onlyIfAbsent) {
- lock();
- try {
- int c = count;
- if (c++ > threshold) // ensure capacity
- rehash();
- HashEntry<K,V>[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> first = tab[index];
- HashEntry<K,V> e = first;
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
-
- V oldValue;
- if (e != null) {
- oldValue = e.value;
- if (!onlyIfAbsent)
- e.value = value;
- }
- else {
- oldValue = null;
- ++modCount;
- tab[index] = new HashEntry<K,V>(key, hash, first, value);
- count = c; // write-volatile
- }
- return oldValue;
- } finally {
- unlock();
- }
- }
-
- void rehash() {
+ private void rehash(HashEntry<K,V> node) {
+ /*
+ * Reclassify nodes in each list to new table. Because we
+ * are using power-of-two expansion, the elements from
+ * each bin must either stay at same index, or move with a
+ * power of two offset. We eliminate unnecessary node
+ * creation by catching cases where old nodes can be
+ * reused because their next fields won't change.
+ * Statistically, at the default threshold, only about
+ * one-sixth of them need cloning when a table
+ * doubles. The nodes they replace will be garbage
+ * collectable as soon as they are no longer referenced by
+ * any reader thread that may be in the midst of
+ * concurrently traversing table. Entry accesses use plain
+ * array indexing because they are followed by volatile
+ * table write.
+ */
HashEntry<K,V>[] oldTable = table;
int oldCapacity = oldTable.length;
- if (oldCapacity >= MAXIMUM_CAPACITY)
- return;
-
- /*
- * Reclassify nodes in each list to new Map. Because we are
- * using power-of-two expansion, the elements from each bin
- * must either stay at same index, or move with a power of two
- * offset. We eliminate unnecessary node creation by catching
- * cases where old nodes can be reused because their next
- * fields won't change. Statistically, at the default
- * threshold, only about one-sixth of them need cloning when
- * a table doubles. The nodes they replace will be garbage
- * collectable as soon as they are no longer referenced by any
- * reader thread that may be in the midst of traversing table
- * right now.
- */
-
- HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1);
- threshold = (int)(newTable.length * loadFactor);
- int sizeMask = newTable.length - 1;
+ int newCapacity = oldCapacity << 1;
+ threshold = (int)(newCapacity * loadFactor);
+ HashEntry<K,V>[] newTable =
+ (HashEntry<K,V>[]) new HashEntry<?,?>[newCapacity];
+ int sizeMask = newCapacity - 1;
for (int i = 0; i < oldCapacity ; i++) {
- // We need to guarantee that any existing reads of old Map can
- // proceed. So we cannot yet null out each bin.
HashEntry<K,V> e = oldTable[i];
-
if (e != null) {
HashEntry<K,V> next = e.next;
int idx = e.hash & sizeMask;
-
- // Single node on list
- if (next == null)
+ if (next == null) // Single node on list
newTable[idx] = e;
-
- else {
- // Reuse trailing consecutive sequence at same slot
+ else { // Reuse consecutive sequence at same slot
HashEntry<K,V> lastRun = e;
int lastIdx = idx;
for (HashEntry<K,V> last = next;
@@ -493,74 +423,263 @@
}
}
newTable[lastIdx] = lastRun;
-
- // Clone all remaining nodes
+ // Clone remaining nodes
for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
- int k = p.hash & sizeMask;
+ V v = p.value;
+ int h = p.hash;
+ int k = h & sizeMask;
HashEntry<K,V> n = newTable[k];
- newTable[k] = new HashEntry<K,V>(p.key, p.hash,
- n, p.value);
+ newTable[k] = new HashEntry<K,V>(h, p.key, v, n);
}
}
}
}
+ int nodeIndex = node.hash & sizeMask; // add the new node
+ node.setNext(newTable[nodeIndex]);
+ newTable[nodeIndex] = node;
table = newTable;
}
/**
+ * Scans for a node containing given key while trying to
+ * acquire lock, creating and returning one if not found. Upon
+ * return, guarantees that lock is held. Unlike in most
+ * methods, calls to method equals are not screened: Since
+ * traversal speed doesn't matter, we might as well help warm
+ * up the associated code and accesses as well.
+ *
+ * @return a new node if key not found, else null
+ */
+ private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
+ HashEntry<K,V> first = entryForHash(this, hash);
+ HashEntry<K,V> e = first;
+ HashEntry<K,V> node = null;
+ int retries = -1; // negative while locating node
+ while (!tryLock()) {
+ HashEntry<K,V> f; // to recheck first below
+ if (retries < 0) {
+ if (e == null) {
+ if (node == null) // speculatively create node
+ node = new HashEntry<K,V>(hash, key, value, null);
+ retries = 0;
+ }
+ else if (key.equals(e.key))
+ retries = 0;
+ else
+ e = e.next;
+ }
+ else if (++retries > MAX_SCAN_RETRIES) {
+ lock();
+ break;
+ }
+ else if ((retries & 1) == 0 &&
+ (f = entryForHash(this, hash)) != first) {
+ e = first = f; // re-traverse if entry changed
+ retries = -1;
+ }
+ }
+ return node;
+ }
+
+ /**
+ * Scans for a node containing the given key while trying to
+ * acquire lock for a remove or replace operation. Upon
+ * return, guarantees that lock is held. Note that we must
+ * lock even if the key is not found, to ensure sequential
+ * consistency of updates.
+ */
+ private void scanAndLock(Object key, int hash) {
+ // similar to but simpler than scanAndLockForPut
+ HashEntry<K,V> first = entryForHash(this, hash);
+ HashEntry<K,V> e = first;
+ int retries = -1;
+ while (!tryLock()) {
+ HashEntry<K,V> f;
+ if (retries < 0) {
+ if (e == null || key.equals(e.key))
+ retries = 0;
+ else
+ e = e.next;
+ }
+ else if (++retries > MAX_SCAN_RETRIES) {
+ lock();
+ break;
+ }
+ else if ((retries & 1) == 0 &&
+ (f = entryForHash(this, hash)) != first) {
+ e = first = f;
+ retries = -1;
+ }
+ }
+ }
+
+ /**
* Remove; match on key only if value null, else match both.
*/
- V remove(Object key, int hash, Object value) {
- lock();
+ final V remove(Object key, int hash, Object value) {
+ if (!tryLock())
+ scanAndLock(key, hash);
+ V oldValue = null;
try {
- int c = count - 1;
HashEntry<K,V>[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> first = tab[index];
- HashEntry<K,V> e = first;
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
+ int index = (tab.length - 1) & hash;
+ HashEntry<K,V> e = entryAt(tab, index);
+ HashEntry<K,V> pred = null;
+ while (e != null) {
+ K k;
+ HashEntry<K,V> next = e.next;
+ if ((k = e.key) == key ||
+ (e.hash == hash && key.equals(k))) {
+ V v = e.value;
+ if (value == null || value == v || value.equals(v)) {
+ if (pred == null)
+ setEntryAt(tab, index, next);
+ else
+ pred.setNext(next);
+ ++modCount;
+ --count;
+ oldValue = v;
+ }
+ break;
+ }
+ pred = e;
+ e = next;
+ }
+ } finally {
+ unlock();
+ }
+ return oldValue;
+ }
- V oldValue = null;
- if (e != null) {
- V v = e.value;
- if (value == null || value.equals(v)) {
- oldValue = v;
- // All entries following removed node can stay
- // in list, but all preceding ones need to be
- // cloned.
- ++modCount;
- HashEntry<K,V> newFirst = e.next;
- for (HashEntry<K,V> p = first; p != e; p = p.next)
- newFirst = new HashEntry<K,V>(p.key, p.hash,
- newFirst, p.value);
- tab[index] = newFirst;
- count = c; // write-volatile
+ final boolean replace(K key, int hash, V oldValue, V newValue) {
+ if (!tryLock())
+ scanAndLock(key, hash);
+ boolean replaced = false;
+ try {
+ HashEntry<K,V> e;
+ for (e = entryForHash(this, hash); e != null; e = e.next) {
+ K k;
+ if ((k = e.key) == key ||
+ (e.hash == hash && key.equals(k))) {
+ if (oldValue.equals(e.value)) {
+ e.value = newValue;
+ ++modCount;
+ replaced = true;
+ }
+ break;
}
}
- return oldValue;
+ } finally {
+ unlock();
+ }
+ return replaced;
+ }
+
+ final V replace(K key, int hash, V value) {
+ if (!tryLock())
+ scanAndLock(key, hash);
+ V oldValue = null;
+ try {
+ HashEntry<K,V> e;
+ for (e = entryForHash(this, hash); e != null; e = e.next) {
+ K k;
+ if ((k = e.key) == key ||
+ (e.hash == hash && key.equals(k))) {
+ oldValue = e.value;
+ e.value = value;
+ ++modCount;
+ break;
+ }
+ }
+ } finally {
+ unlock();
+ }
+ return oldValue;
+ }
+
+ final void clear() {
+ lock();
+ try {
+ HashEntry<K,V>[] tab = table;
+ for (int i = 0; i < tab.length ; i++)
+ setEntryAt(tab, i, null);
+ ++modCount;
+ count = 0;
} finally {
unlock();
}
}
+ }
- void clear() {
- if (count != 0) {
- lock();
- try {
- HashEntry<K,V>[] tab = table;
- for (int i = 0; i < tab.length ; i++)
- tab[i] = null;
- ++modCount;
- count = 0; // write-volatile
- } finally {
- unlock();
+ // Accessing segments
+
+ /**
+ * Gets the jth element of given segment array (if nonnull) with
+ * volatile element access semantics via Unsafe. (The null check
+ * can trigger harmlessly only during deserialization.) Note:
+ * because each element of segments array is set only once (using
+ * fully ordered writes), some performance-sensitive methods rely
+ * on this method only as a recheck upon null reads.
+ */
+ @SuppressWarnings("unchecked")
+ static final <K,V> Segment<K,V> segmentAt(Segment<K,V>[] ss, int j) {
+ long u = (j << SSHIFT) + SBASE;
+ return ss == null ? null :
+ (Segment<K,V>) UNSAFE.getObjectVolatile(ss, u);
+ }
+
+ /**
+ * Returns the segment for the given index, creating it and
+ * recording in segment table (via CAS) if not already present.
+ *
+ * @param k the index
+ * @return the segment
+ */
+ @SuppressWarnings("unchecked")
+ private Segment<K,V> ensureSegment(int k) {
+ final Segment<K,V>[] ss = this.segments;
+ long u = (k << SSHIFT) + SBASE; // raw offset
+ Segment<K,V> seg;
+ if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {
+ Segment<K,V> proto = ss[0]; // use segment 0 as prototype
+ int cap = proto.table.length;
+ float lf = proto.loadFactor;
+ int threshold = (int)(cap * lf);
+ HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry<?,?>[cap];
+ if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
+ == null) { // recheck
+ Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);
+ while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
+ == null) {
+ if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))
+ break;
}
}
}
+ return seg;
}
+ // Hash-based segment and entry accesses
+ /**
+ * Gets the segment for the given hash code.
+ */
+ @SuppressWarnings("unchecked")
+ private Segment<K,V> segmentForHash(int h) {
+ long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
+ return (Segment<K,V>) UNSAFE.getObjectVolatile(segments, u);
+ }
+
+ /**
+ * Gets the table entry for the given segment and hash code.
+ */
+ @SuppressWarnings("unchecked")
+ static final <K,V> HashEntry<K,V> entryForHash(Segment<K,V> seg, int h) {
+ HashEntry<K,V>[] tab;
+ return (seg == null || (tab = seg.table) == null) ? null :
+ (HashEntry<K,V>) UNSAFE.getObjectVolatile
+ (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
+ }
/* ---------------- Public operations -------------- */
@@ -580,14 +699,13 @@
* negative or the load factor or concurrencyLevel are
* nonpositive.
*/
+ @SuppressWarnings("unchecked")
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
-
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;
-
// Find power-of-two sizes best matching arguments
int sshift = 0;
int ssize = 1;
@@ -595,21 +713,23 @@
++sshift;
ssize <<= 1;
}
- segmentShift = 32 - sshift;
- segmentMask = ssize - 1;
- this.segments = Segment.newArray(ssize);
-
+ this.segmentShift = 32 - sshift;
+ this.segmentMask = ssize - 1;
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
- int cap = 1;
+ int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
cap <<= 1;
-
- for (int i = 0; i < this.segments.length; ++i)
- this.segments[i] = new Segment<K,V>(cap, loadFactor);
+ // create segments and segments[0]
+ Segment<K,V> s0 =
+ new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
+ (HashEntry<K,V>[])new HashEntry<?,?>[cap]);
+ Segment<K,V>[] ss = (Segment<K,V>[])new Segment<?,?>[ssize];
+ UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
+ this.segments = ss;
}
/**
@@ -672,34 +792,37 @@
* @return <tt>true</tt> if this map contains no key-value mappings
*/
public boolean isEmpty() {
- final Segment<K,V>[] segments = this.segments;
/*
- * We keep track of per-segment modCounts to avoid ABA
- * problems in which an element in one segment was added and
- * in another removed during traversal, in which case the
- * table was never actually empty at any point. Note the
- * similar use of modCounts in the size() and containsValue()
- * methods, which are the only other methods also susceptible
- * to ABA problems.
+ * Sum per-segment modCounts to avoid mis-reporting when
+ * elements are concurrently added and removed in one segment
+ * while checking another, in which case the table was never
+ * actually empty at any point. (The sum ensures accuracy up
+ * through at least 1<<31 per-segment modifications before
+ * recheck.) Methods size() and containsValue() use similar
+ * constructions for stability checks.
*/
- int[] mc = new int[segments.length];
- int mcsum = 0;
- for (int i = 0; i < segments.length; ++i) {
- if (segments[i].count != 0)
- return false;
- else
- mcsum += mc[i] = segments[i].modCount;
- }
- // If mcsum happens to be zero, then we know we got a snapshot
- // before any modifications at all were made. This is
- // probably common enough to bother tracking.
- if (mcsum != 0) {
- for (int i = 0; i < segments.length; ++i) {
- if (segments[i].count != 0 ||
- mc[i] != segments[i].modCount)
+ long sum = 0L;
+ final Segment<K,V>[] segments = this.segments;
+ for (int j = 0; j < segments.length; ++j) {
+ Segment<K,V> seg = segmentAt(segments, j);
+ if (seg != null) {
+ if (seg.count != 0)
return false;
+ sum += seg.modCount;
}
}
+ if (sum != 0L) { // recheck unless no modifications
+ for (int j = 0; j < segments.length; ++j) {
+ Segment<K,V> seg = segmentAt(segments, j);
+ if (seg != null) {
+ if (seg.count != 0)
+ return false;
+ sum -= seg.modCount;
+ }
+ }
+ if (sum != 0L)
+ return false;
+ }
return true;
}
@@ -711,45 +834,36 @@
* @return the number of key-value mappings in this map
*/
public int size() {
- final Segment<K,V>[] segments = this.segments;
- long sum = 0;
- long check = 0;
- int[] mc = new int[segments.length];
// Try a few times to get accurate count. On failure due to
// continuous async changes in table, resort to locking.
- for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
- check = 0;
- sum = 0;
- int mcsum = 0;
- for (int i = 0; i < segments.length; ++i) {
- sum += segments[i].count;
- mcsum += mc[i] = segments[i].modCount;
- }
- if (mcsum != 0) {
- for (int i = 0; i < segments.length; ++i) {
- check += segments[i].count;
- if (mc[i] != segments[i].modCount) {
- check = -1; // force retry
- break;
- }
+ final Segment<K,V>[] segments = this.segments;
+ final int segmentCount = segments.length;
+
+ long previousSum = 0L;
+ for (int retries = -1; retries < RETRIES_BEFORE_LOCK; retries++) {
+ long sum = 0L; // sum of modCounts
+ long size = 0L;
+ for (int i = 0; i < segmentCount; i++) {
+ Segment<K,V> segment = segmentAt(segments, i);
+ if (segment != null) {
+ sum += segment.modCount;
+ size += segment.count;
}
}
- if (check == sum)
- break;
+ if (sum == previousSum)
+ return ((size >>> 31) == 0) ? (int) size : Integer.MAX_VALUE;
+ previousSum = sum;
}
- if (check != sum) { // Resort to locking all segments
- sum = 0;
- for (int i = 0; i < segments.length; ++i)
- segments[i].lock();
- for (int i = 0; i < segments.length; ++i)
- sum += segments[i].count;
- for (int i = 0; i < segments.length; ++i)
- segments[i].unlock();
+
+ long size = 0L;
+ for (int i = 0; i < segmentCount; i++) {
+ Segment<K,V> segment = ensureSegment(i);
+ segment.lock();
+ size += segment.count;
}
- if (sum > Integer.MAX_VALUE)
- return Integer.MAX_VALUE;
- else
- return (int)sum;
+ for (int i = 0; i < segmentCount; i++)
+ segments[i].unlock();
+ return ((size >>> 31) == 0) ? (int) size : Integer.MAX_VALUE;
}
/**
@@ -764,8 +878,21 @@
* @throws NullPointerException if the specified key is null
*/
public V get(Object key) {
- int hash = hash(key.hashCode());
- return segmentFor(hash).get(key, hash);
+ Segment<K,V> s; // manually integrate access methods to reduce overhead
+ HashEntry<K,V>[] tab;
+ int h = hash(key.hashCode());
+ long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
+ if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
+ (tab = s.table) != null) {
+ for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
+ (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
+ e != null; e = e.next) {
+ K k;
+ if ((k = e.key) == key || (e.hash == h && key.equals(k)))
+ return e.value;
+ }
+ }
+ return null;
}
/**
@@ -777,9 +904,23 @@
* <tt>equals</tt> method; <tt>false</tt> otherwise.
* @throws NullPointerException if the specified key is null
*/
+ @SuppressWarnings("unchecked")
public boolean containsKey(Object key) {
- int hash = hash(key.hashCode());
- return segmentFor(hash).containsKey(key, hash);
+ Segment<K,V> s; // same as get() except no need for volatile value read
+ HashEntry<K,V>[] tab;
+ int h = hash(key.hashCode());
+ long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
+ if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
+ (tab = s.table) != null) {
+ for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
+ (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
+ e != null; e = e.next) {
+ K k;
+ if ((k = e.key) == key || (e.hash == h && key.equals(k)))
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -794,53 +935,47 @@
* @throws NullPointerException if the specified value is null
*/
public boolean containsValue(Object value) {
+ // Same idea as size()
if (value == null)
throw new NullPointerException();
-
- // See explanation of modCount use above
-
final Segment<K,V>[] segments = this.segments;
- int[] mc = new int[segments.length];
-
- // Try a few times without locking
- for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
- int sum = 0;
- int mcsum = 0;
- for (int i = 0; i < segments.length; ++i) {
- int c = segments[i].count;
- mcsum += mc[i] = segments[i].modCount;
- if (segments[i].containsValue(value))
- return true;
- }
- boolean cleanSweep = true;
- if (mcsum != 0) {
- for (int i = 0; i < segments.length; ++i) {
- int c = segments[i].count;
- if (mc[i] != segments[i].modCount) {
- cleanSweep = false;
- break;
+ long previousSum = 0L;
+ int lockCount = 0;
+ try {
+ for (int retries = -1; ; retries++) {
+ long sum = 0L; // sum of modCounts
+ for (int j = 0; j < segments.length; j++) {
+ Segment<K,V> segment;
+ if (retries == RETRIES_BEFORE_LOCK) {
+ segment = ensureSegment(j);
+ segment.lock();
+ lockCount++;
+ } else {
+ segment = segmentAt(segments, j);
+ if (segment == null)
+ continue;
+ }
+ HashEntry<K,V>[] tab = segment.table;
+ if (tab != null) {
+ for (int i = 0 ; i < tab.length; i++) {
+ HashEntry<K,V> e;
+ for (e = entryAt(tab, i); e != null; e = e.next) {
+ V v = e.value;
+ if (v != null && value.equals(v))
+ return true;
+ }
+ }
+ sum += segment.modCount;
}
}
- }
- if (cleanSweep)
- return false;
- }
- // Resort to locking all segments
- for (int i = 0; i < segments.length; ++i)
- segments[i].lock();
- boolean found = false;
- try {
- for (int i = 0; i < segments.length; ++i) {
- if (segments[i].containsValue(value)) {
- found = true;
- break;
- }
+ if ((retries >= 0 && sum == previousSum) || lockCount > 0)
+ return false;
+ previousSum = sum;
}
} finally {
- for (int i = 0; i < segments.length; ++i)
- segments[i].unlock();
+ for (int j = 0; j < lockCount; j++)
+ segments[j].unlock();
}
- return found;
}
/**
@@ -850,7 +985,7 @@
* full compatibility with class {@link java.util.Hashtable},
* which supported this method prior to introduction of the
* Java Collections framework.
-
+ *
* @param value a value to search for
* @return <tt>true</tt> if and only if some key maps to the
* <tt>value</tt> argument in this table as
@@ -875,11 +1010,17 @@
* <tt>null</tt> if there was no mapping for <tt>key</tt>
* @throws NullPointerException if the specified key or value is null
*/
+ @SuppressWarnings("unchecked")
public V put(K key, V value) {
+ Segment<K,V> s;
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
- return segmentFor(hash).put(key, hash, value, false);
+ int j = (hash >>> segmentShift) & segmentMask;
+ if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
+ (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
+ s = ensureSegment(j);
+ return s.put(key, hash, value, false);
}
/**
@@ -889,11 +1030,17 @@
* or <tt>null</tt> if there was no mapping for the key
* @throws NullPointerException if the specified key or value is null
*/
+ @SuppressWarnings("unchecked")
public V putIfAbsent(K key, V value) {
+ Segment<K,V> s;
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
- return segmentFor(hash).put(key, hash, value, true);
+ int j = (hash >>> segmentShift) & segmentMask;
+ if ((s = (Segment<K,V>)UNSAFE.getObject
+ (segments, (j << SSHIFT) + SBASE)) == null)
+ s = ensureSegment(j);
+ return s.put(key, hash, value, true);
}
/**
@@ -919,7 +1066,8 @@
*/
public V remove(Object key) {
int hash = hash(key.hashCode());
- return segmentFor(hash).remove(key, hash, null);
+ Segment<K,V> s = segmentForHash(hash);
+ return s == null ? null : s.remove(key, hash, null);
}
/**
@@ -929,9 +1077,9 @@
*/
public boolean remove(Object key, Object value) {
int hash = hash(key.hashCode());
- if (value == null)
- return false;
- return segmentFor(hash).remove(key, hash, value) != null;
+ Segment<K,V> s;
+ return value != null && (s = segmentForHash(hash)) != null &&
+ s.remove(key, hash, value) != null;
}
/**
@@ -940,10 +1088,11 @@
* @throws NullPointerException if any of the arguments are null
*/
public boolean replace(K key, V oldValue, V newValue) {
+ int hash = hash(key.hashCode());
if (oldValue == null || newValue == null)
throw new NullPointerException();
- int hash = hash(key.hashCode());
- return segmentFor(hash).replace(key, hash, oldValue, newValue);
+ Segment<K,V> s = segmentForHash(hash);
+ return s != null && s.replace(key, hash, oldValue, newValue);
}
/**
@@ -954,18 +1103,23 @@
* @throws NullPointerException if the specified key or value is null
*/
public V replace(K key, V value) {
+ int hash = hash(key.hashCode());
if (value == null)
throw new NullPointerException();
- int hash = hash(key.hashCode());
- return segmentFor(hash).replace(key, hash, value);
+ Segment<K,V> s = segmentForHash(hash);
+ return s == null ? null : s.replace(key, hash, value);
}
/**
* Removes all of the mappings from this map.
*/
public void clear() {
- for (int i = 0; i < segments.length; ++i)
- segments[i].clear();
+ final Segment<K,V>[] segments = this.segments;
+ for (int j = 0; j < segments.length; ++j) {
+ Segment<K,V> s = segmentAt(segments, j);
+ if (s != null)
+ s.clear();
+ }
}
/**
@@ -1066,42 +1220,41 @@
advance();
}
- public boolean hasMoreElements() { return hasNext(); }
-
+ /**
+ * Sets nextEntry to first node of next non-empty table
+ * (in backwards order, to simplify checks).
+ */
final void advance() {
- if (nextEntry != null && (nextEntry = nextEntry.next) != null)
- return;
-
- while (nextTableIndex >= 0) {
- if ( (nextEntry = currentTable[nextTableIndex--]) != null)
- return;
- }
-
- while (nextSegmentIndex >= 0) {
- Segment<K,V> seg = segments[nextSegmentIndex--];
- if (seg.count != 0) {
- currentTable = seg.table;
- for (int j = currentTable.length - 1; j >= 0; --j) {
- if ( (nextEntry = currentTable[j]) != null) {
- nextTableIndex = j - 1;
- return;
- }
- }
+ for (;;) {
+ if (nextTableIndex >= 0) {
+ if ((nextEntry = entryAt(currentTable,
+ nextTableIndex--)) != null)
+ break;
}
+ else if (nextSegmentIndex >= 0) {
+ Segment<K,V> seg = segmentAt(segments, nextSegmentIndex--);
+ if (seg != null && (currentTable = seg.table) != null)
+ nextTableIndex = currentTable.length - 1;
+ }
+ else
+ break;
}
}
- public boolean hasNext() { return nextEntry != null; }
-
- HashEntry<K,V> nextEntry() {
- if (nextEntry == null)
+ final HashEntry<K,V> nextEntry() {
+ HashEntry<K,V> e = nextEntry;
+ if (e == null)
throw new NoSuchElementException();
- lastReturned = nextEntry;
- advance();
- return lastReturned;
+ lastReturned = e; // cannot assign until after null check
+ if ((nextEntry = e.next) == null)
+ advance();
+ return e;
}
- public void remove() {
+ public final boolean hasNext() { return nextEntry != null; }
+ public final boolean hasMoreElements() { return nextEntry != null; }
+
+ public final void remove() {
if (lastReturned == null)
throw new IllegalStateException();
ConcurrentHashMap.this.remove(lastReturned.key);
@@ -1113,16 +1266,16 @@
extends HashIterator
implements Iterator<K>, Enumeration<K>
{
- public K next() { return super.nextEntry().key; }
- public K nextElement() { return super.nextEntry().key; }
+ public final K next() { return super.nextEntry().key; }
+ public final K nextElement() { return super.nextEntry().key; }
}
final class ValueIterator
extends HashIterator
implements Iterator<V>, Enumeration<V>
{
- public V next() { return super.nextEntry().value; }
- public V nextElement() { return super.nextEntry().value; }
+ public final V next() { return super.nextEntry().value; }
+ public final V nextElement() { return super.nextEntry().value; }
}
/**
@@ -1137,7 +1290,7 @@
}
/**
- * Set our entry's value and write through to the map. The
+ * Sets our entry's value and writes through to the map. The
* value to return is somewhat arbitrary here. Since a
* WriteThroughEntry does not necessarily track asynchronous
* changes, the most recent "previous" value could be
@@ -1233,24 +1386,30 @@
/* ---------------- Serialization Support -------------- */
/**
- * Save the state of the <tt>ConcurrentHashMap</tt> instance to a
- * stream (i.e., serialize it).
+ * Saves the state of the <tt>ConcurrentHashMap</tt> instance to a
+ * stream (i.e., serializes it).
* @param s the stream
* @serialData
* the key (Object) and value (Object)
* for each key-value mapping, followed by a null pair.
* The key-value mappings are emitted in no particular order.
*/
- private void writeObject(java.io.ObjectOutputStream s) throws IOException {
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ // force all segments for serialization compatibility
+ for (int k = 0; k < segments.length; ++k)
+ ensureSegment(k);
s.defaultWriteObject();
+ final Segment<K,V>[] segments = this.segments;
for (int k = 0; k < segments.length; ++k) {
- Segment<K,V> seg = segments[k];
+ Segment<K,V> seg = segmentAt(segments, k);
seg.lock();
try {
HashEntry<K,V>[] tab = seg.table;
for (int i = 0; i < tab.length; ++i) {
- for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
+ HashEntry<K,V> e;
+ for (e = entryAt(tab, i); e != null; e = e.next) {
s.writeObject(e.key);
s.writeObject(e.value);
}
@@ -1264,17 +1423,24 @@
}
/**
- * Reconstitute the <tt>ConcurrentHashMap</tt> instance from a
- * stream (i.e., deserialize it).
+ * Reconstitutes the <tt>ConcurrentHashMap</tt> instance from a
+ * stream (i.e., deserializes it).
* @param s the stream
*/
+ @SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)
- throws IOException, ClassNotFoundException {
+ throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
- // Initialize each segment to be minimally sized, and let grow.
- for (int i = 0; i < segments.length; ++i) {
- segments[i].setTable(new HashEntry[1]);
+ // Re-initialize segments to be minimally sized, and let grow.
+ int cap = MIN_SEGMENT_TABLE_CAPACITY;
+ final Segment<K,V>[] segments = this.segments;
+ for (int k = 0; k < segments.length; ++k) {
+ Segment<K,V> seg = segments[k];
+ if (seg != null) {
+ seg.threshold = (int)(cap * seg.loadFactor);
+ seg.table = (HashEntry<K,V>[]) new HashEntry<?,?>[cap];
+ }
}
// Read the keys and values, and put the mappings in the table
@@ -1286,4 +1452,31 @@
put(key, value);
}
}
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long SBASE;
+ private static final int SSHIFT;
+ private static final long TBASE;
+ private static final int TSHIFT;
+
+ static {
+ int ss, ts;
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> tc = HashEntry[].class;
+ Class<?> sc = Segment[].class;
+ TBASE = UNSAFE.arrayBaseOffset(tc);
+ SBASE = UNSAFE.arrayBaseOffset(sc);
+ ts = UNSAFE.arrayIndexScale(tc);
+ ss = UNSAFE.arrayIndexScale(sc);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ if ((ss & (ss-1)) != 0 || (ts & (ts-1)) != 0)
+ throw new Error("data type scale not a power of two");
+ SSHIFT = 31 - Integer.numberOfLeadingZeros(ss);
+ TSHIFT = 31 - Integer.numberOfLeadingZeros(ts);
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
index 72c2e08..30adb36 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea and Martin Buchholz with assistance from members of
* JCP JSR-166 Expert Group and released to the public domain, as explained
- * at http://creativecommons.org/licenses/publicdomain
+ * at http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -34,10 +34,17 @@
* ConcurrentModificationException}, and may proceed concurrently with
* other operations.
*
- * <p>Beware that, unlike in most collections, the {@code size}
- * method is <em>NOT</em> a constant-time operation. Because of the
+ * <p>Beware that, unlike in most collections, the {@code size} method
+ * is <em>NOT</em> a constant-time operation. Because of the
* asynchronous nature of these deques, determining the current number
- * of elements requires a traversal of the elements.
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
*
* <p>This class and its iterator implement all of the <em>optional</em>
* methods of the {@link Deque} and {@link Iterator} interfaces.
@@ -245,13 +252,6 @@
private static final Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR;
- static {
- PREV_TERMINATOR = new Node<Object>(null);
- PREV_TERMINATOR.next = PREV_TERMINATOR;
- NEXT_TERMINATOR = new Node<Object>(null);
- NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
- }
-
@SuppressWarnings("unchecked")
Node<E> prevTerminator() {
return (Node<E>) PREV_TERMINATOR;
@@ -267,6 +267,9 @@
volatile E item;
volatile Node<E> next;
+ Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR
+ }
+
/**
* Constructs a new node. Uses relaxed write because item can
* only be seen after publication via casNext or casPrev.
@@ -297,14 +300,25 @@
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE =
- sun.misc.Unsafe.getUnsafe();
- private static final long prevOffset =
- objectFieldOffset(UNSAFE, "prev", Node.class);
- private static final long itemOffset =
- objectFieldOffset(UNSAFE, "item", Node.class);
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", Node.class);
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long prevOffset;
+ private static final long itemOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Node.class;
+ prevOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("prev"));
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/**
@@ -1209,8 +1223,7 @@
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -1395,14 +1408,6 @@
initHeadTail(h, t);
}
- // Unsafe mechanics
-
- private static final sun.misc.Unsafe UNSAFE =
- sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", ConcurrentLinkedDeque.class);
- private static final long tailOffset =
- objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedDeque.class);
private boolean casHead(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
@@ -1412,15 +1417,25 @@
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ private static final long tailOffset;
+ static {
+ PREV_TERMINATOR = new Node<Object>();
+ PREV_TERMINATOR.next = PREV_TERMINATOR;
+ NEXT_TERMINATOR = new Node<Object>();
+ NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ConcurrentLinkedDeque.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
index 3ed0a7c..a0a26fd 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea and Martin Buchholz with assistance from members of
* JCP JSR-166 Expert Group and released to the public domain, as explained
- * at http://creativecommons.org/licenses/publicdomain
+ * at http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -47,7 +47,14 @@
* <p>Beware that, unlike in most collections, the {@code size} method
* is <em>NOT</em> a constant-time operation. Because of the
* asynchronous nature of these queues, determining the current number
- * of elements requires a traversal of the elements.
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
*
* <p>This class and its iterator implement all of the <em>optional</em>
* methods of the {@link Queue} and {@link Iterator} interfaces.
@@ -165,12 +172,22 @@
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE =
- sun.misc.Unsafe.getUnsafe();
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", Node.class);
- private static final long itemOffset =
- objectFieldOffset(UNSAFE, "item", Node.class);
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long itemOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Node.class;
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/**
@@ -563,8 +580,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -761,14 +777,6 @@
throw new NullPointerException();
}
- // Unsafe mechanics
-
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", ConcurrentLinkedQueue.class);
- private static final long tailOffset =
- objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedQueue.class);
-
private boolean casTail(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
@@ -777,15 +785,21 @@
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
}
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ private static final long tailOffset;
+ static {
try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ConcurrentLinkedQueue.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
index 2daebc5..3405acf 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -32,11 +32,12 @@
* If the specified key is not already associated
* with a value, associate it with the given value.
* This is equivalent to
- * <pre>
- * if (!map.containsKey(key))
- * return map.put(key, value);
- * else
- * return map.get(key);</pre>
+ * <pre> {@code
+ * if (!map.containsKey(key))
+ * return map.put(key, value);
+ * else
+ * return map.get(key);}</pre>
+ *
* except that the action is performed atomically.
*
* @param key key with which the specified value is to be associated
@@ -61,11 +62,13 @@
/**
* Removes the entry for a key only if currently mapped to a given value.
* This is equivalent to
- * <pre>
- * if (map.containsKey(key) && map.get(key).equals(value)) {
- * map.remove(key);
- * return true;
- * } else return false;</pre>
+ * <pre> {@code
+ * if (map.containsKey(key) && map.get(key).equals(value)) {
+ * map.remove(key);
+ * return true;
+ * } else
+ * return false;}</pre>
+ *
* except that the action is performed atomically.
*
* @param key key with which the specified value is associated
@@ -74,20 +77,24 @@
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this map
* @throws ClassCastException if the key or value is of an inappropriate
- * type for this map (optional)
+ * type for this map
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key or value is null,
- * and this map does not permit null keys or values (optional)
+ * and this map does not permit null keys or values
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object key, Object value);
/**
* Replaces the entry for a key only if currently mapped to a given value.
* This is equivalent to
- * <pre>
- * if (map.containsKey(key) && map.get(key).equals(oldValue)) {
- * map.put(key, newValue);
- * return true;
- * } else return false;</pre>
+ * <pre> {@code
+ * if (map.containsKey(key) && map.get(key).equals(oldValue)) {
+ * map.put(key, newValue);
+ * return true;
+ * } else
+ * return false;}</pre>
+ *
* except that the action is performed atomically.
*
* @param key key with which the specified value is associated
@@ -108,10 +115,12 @@
/**
* Replaces the entry for a key only if currently mapped to some value.
* This is equivalent to
- * <pre>
- * if (map.containsKey(key)) {
- * return map.put(key, value);
- * } else return null;</pre>
+ * <pre> {@code
+ * if (map.containsKey(key)) {
+ * return map.put(key, value);
+ * } else
+ * return null;}</pre>
+ *
* except that the action is performed atomically.
*
* @param key key with which the specified value is associated
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
index 7d86afb..ea99886 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
@@ -1,20 +1,20 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.*;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
* A {@link ConcurrentMap} supporting {@link NavigableMap} operations,
* and recursively so for its navigable sub-maps.
*
- * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
index fbd1083..d0d6f14 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
@@ -1,12 +1,15 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.*;
-import java.util.concurrent.atomic.*;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* A scalable concurrent {@link ConcurrentNavigableMap} implementation.
@@ -15,8 +18,8 @@
* creation time, depending on which constructor is used.
*
* <p>This class implements a concurrent variant of <a
- * href="http://www.cs.umd.edu/~pugh/">SkipLists</a> providing
- * expected average <i>log(n)</i> time cost for the
+ * href="http://en.wikipedia.org/wiki/Skip_list" target="_top">SkipLists</a>
+ * providing expected average <i>log(n)</i> time cost for the
* <tt>containsKey</tt>, <tt>get</tt>, <tt>put</tt> and
* <tt>remove</tt> operations and their variants. Insertion, removal,
* update, and access operations safely execute concurrently by
@@ -37,12 +40,13 @@
* <p>Beware that, unlike in most collections, the <tt>size</tt>
* method is <em>not</em> a constant-time operation. Because of the
* asynchronous nature of these maps, determining the current number
- * of elements requires a traversal of the elements. Additionally,
- * the bulk operations <tt>putAll</tt>, <tt>equals</tt>, and
- * <tt>clear</tt> are <em>not</em> guaranteed to be performed
- * atomically. For example, an iterator operating concurrently with a
- * <tt>putAll</tt> operation might view only some of the added
- * elements.
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations <tt>putAll</tt>, <tt>equals</tt>,
+ * <tt>toArray</tt>, <tt>containsValue</tt>, and <tt>clear</tt> are
+ * <em>not</em> guaranteed to be performed atomically. For example, an
+ * iterator operating concurrently with a <tt>putAll</tt> operation
+ * might view only some of the added elements.
*
* <p>This class and its views and iterators implement all of the
* <em>optional</em> methods of the {@link Map} and {@link Iterator}
@@ -51,10 +55,6 @@
* null return values cannot be reliably distinguished from the absence of
* elements.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
@@ -322,11 +322,11 @@
private transient int randomSeed;
/** Lazily initialized key set */
- private transient KeySet keySet;
+ private transient KeySet<K> keySet;
/** Lazily initialized entry set */
- private transient EntrySet entrySet;
+ private transient EntrySet<K,V> entrySet;
/** Lazily initialized values collection */
- private transient Values values;
+ private transient Values<V> values;
/** Lazily initialized descending key set */
private transient ConcurrentNavigableMap<K,V> descendingMap;
@@ -478,13 +478,24 @@
return new AbstractMap.SimpleImmutableEntry<K,V>(key, v);
}
- // Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long valueOffset =
- objectFieldOffset(UNSAFE, "value", Node.class);
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", Node.class);
+ // UNSAFE mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long valueOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Node.class;
+ valueOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("value"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/* ---------------- Indexing -------------- */
@@ -551,10 +562,18 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long rightOffset =
- objectFieldOffset(UNSAFE, "right", Index.class);
-
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long rightOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Index.class;
+ rightOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("right"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/* ---------------- Head nodes -------------- */
@@ -884,7 +903,7 @@
* direction.
*/
level = max + 1;
- Index<K,V>[] idxs = (Index<K,V>[])new Index[level+1];
+ Index<K,V>[] idxs = (Index<K,V>[])new Index<?,?>[level+1];
Index<K,V> idx = null;
for (int i = 1; i <= level; ++i)
idxs[i] = idx = new Index<K,V>(z, idx, null);
@@ -1387,16 +1406,16 @@
* @return a shallow copy of this map
*/
public ConcurrentSkipListMap<K,V> clone() {
- ConcurrentSkipListMap<K,V> clone = null;
try {
- clone = (ConcurrentSkipListMap<K,V>) super.clone();
+ @SuppressWarnings("unchecked")
+ ConcurrentSkipListMap<K,V> clone =
+ (ConcurrentSkipListMap<K,V>) super.clone();
+ clone.initialize();
+ clone.buildFromSorted(this);
+ return clone;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
-
- clone.initialize();
- clone.buildFromSorted(this);
- return clone;
}
/**
@@ -1458,7 +1477,7 @@
/* ---------------- Serialization -------------- */
/**
- * Save the state of this map to a stream.
+ * Saves the state of this map to a stream (that is, serializes it).
*
* @serialData The key (Object) and value (Object) for each
* key-value mapping represented by the map, followed by
@@ -1483,7 +1502,9 @@
}
/**
- * Reconstitute the map from a stream.
+ * Reconstitutes the map from a stream (that is, deserializes it).
+ *
+ * @param s the stream
*/
private void readObject(final java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1613,7 +1634,9 @@
/**
* Returns <tt>true</tt> if this map maps one or more keys to the
* specified value. This operation requires time linear in the
- * map size.
+ * map size. Additionally, it is possible for the map to change
+ * during execution of this method, in which case the returned
+ * result may be inaccurate.
*
* @param value value whose presence in this map is to be tested
* @return <tt>true</tt> if a mapping to <tt>value</tt> exists;
@@ -1703,14 +1726,14 @@
*
* @return a navigable set view of the keys in this map
*/
- public NavigableSet<K> keySet() {
- KeySet ks = keySet;
- return (ks != null) ? ks : (keySet = new KeySet(this));
+ public NavigableSet<K> keySet() {
+ KeySet<K> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet<K>(this));
}
public NavigableSet<K> navigableKeySet() {
- KeySet ks = keySet;
- return (ks != null) ? ks : (keySet = new KeySet(this));
+ KeySet<K> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet<K>(this));
}
/**
@@ -1732,8 +1755,8 @@
* reflect any modifications subsequent to construction.
*/
public Collection<V> values() {
- Values vs = values;
- return (vs != null) ? vs : (values = new Values(this));
+ Values<V> vs = values;
+ return (vs != null) ? vs : (values = new Values<V>(this));
}
/**
@@ -1761,8 +1784,8 @@
* sorted in ascending key order
*/
public Set<Map.Entry<K,V>> entrySet() {
- EntrySet es = entrySet;
- return (es != null) ? es : (entrySet = new EntrySet(this));
+ EntrySet<K,V> es = entrySet;
+ return (es != null) ? es : (entrySet = new EntrySet<K,V>(this));
}
public ConcurrentNavigableMap<K,V> descendingMap() {
@@ -2253,8 +2276,8 @@
static final class KeySet<E>
extends AbstractSet<E> implements NavigableSet<E> {
- private final ConcurrentNavigableMap<E,Object> m;
- KeySet(ConcurrentNavigableMap<E,Object> map) { m = map; }
+ private final ConcurrentNavigableMap<E,?> m;
+ KeySet(ConcurrentNavigableMap<E,?> map) { m = map; }
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean contains(Object o) { return m.containsKey(o); }
@@ -2268,11 +2291,11 @@
public E first() { return m.firstKey(); }
public E last() { return m.lastKey(); }
public E pollFirst() {
- Map.Entry<E,Object> e = m.pollFirstEntry();
+ Map.Entry<E,?> e = m.pollFirstEntry();
return (e == null) ? null : e.getKey();
}
public E pollLast() {
- Map.Entry<E,Object> e = m.pollLastEntry();
+ Map.Entry<E,?> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
}
public Iterator<E> iterator() {
@@ -2323,20 +2346,20 @@
return tailSet(fromElement, true);
}
public NavigableSet<E> descendingSet() {
- return new KeySet(m.descendingMap());
+ return new KeySet<E>(m.descendingMap());
}
}
static final class Values<E> extends AbstractCollection<E> {
- private final ConcurrentNavigableMap<Object, E> m;
- Values(ConcurrentNavigableMap<Object, E> map) {
+ private final ConcurrentNavigableMap<?, E> m;
+ Values(ConcurrentNavigableMap<?, E> map) {
m = map;
}
public Iterator<E> iterator() {
if (m instanceof ConcurrentSkipListMap)
- return ((ConcurrentSkipListMap<Object,E>)m).valueIterator();
+ return ((ConcurrentSkipListMap<?,E>)m).valueIterator();
else
- return ((SubMap<Object,E>)m).valueIterator();
+ return ((SubMap<?,E>)m).valueIterator();
}
public boolean isEmpty() {
return m.isEmpty();
@@ -2370,14 +2393,14 @@
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
- Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o;
+ Map.Entry<?,?> e = (Map.Entry<?,?>)o;
V1 v = m.get(e.getKey());
return v != null && v.equals(e.getValue());
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
- Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o;
+ Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return m.remove(e.getKey(),
e.getValue());
}
@@ -2517,9 +2540,9 @@
if (lo == null)
return m.findFirst();
else if (loInclusive)
- return m.findNear(lo, m.GT|m.EQ);
+ return m.findNear(lo, GT|EQ);
else
- return m.findNear(lo, m.GT);
+ return m.findNear(lo, GT);
}
/**
@@ -2530,9 +2553,9 @@
if (hi == null)
return m.findLast();
else if (hiInclusive)
- return m.findNear(hi, m.LT|m.EQ);
+ return m.findNear(hi, LT|EQ);
else
- return m.findNear(hi, m.LT);
+ return m.findNear(hi, LT);
}
/**
@@ -2614,15 +2637,15 @@
*/
private Map.Entry<K,V> getNearEntry(K key, int rel) {
if (isDescending) { // adjust relation for direction
- if ((rel & m.LT) == 0)
- rel |= m.LT;
+ if ((rel & LT) == 0)
+ rel |= LT;
else
- rel &= ~m.LT;
+ rel &= ~LT;
}
if (tooLow(key))
- return ((rel & m.LT) != 0) ? null : lowestEntry();
+ return ((rel & LT) != 0) ? null : lowestEntry();
if (tooHigh(key))
- return ((rel & m.LT) != 0) ? highestEntry() : null;
+ return ((rel & LT) != 0) ? highestEntry() : null;
for (;;) {
Node<K,V> n = m.findNear(key, rel);
if (n == null || !inBounds(n.key))
@@ -2637,13 +2660,13 @@
// Almost the same as getNearEntry, except for keys
private K getNearKey(K key, int rel) {
if (isDescending) { // adjust relation for direction
- if ((rel & m.LT) == 0)
- rel |= m.LT;
+ if ((rel & LT) == 0)
+ rel |= LT;
else
- rel &= ~m.LT;
+ rel &= ~LT;
}
if (tooLow(key)) {
- if ((rel & m.LT) == 0) {
+ if ((rel & LT) == 0) {
ConcurrentSkipListMap.Node<K,V> n = loNode();
if (isBeforeEnd(n))
return n.key;
@@ -2651,7 +2674,7 @@
return null;
}
if (tooHigh(key)) {
- if ((rel & m.LT) != 0) {
+ if ((rel & LT) != 0) {
ConcurrentSkipListMap.Node<K,V> n = hiNode();
if (n != null) {
K last = n.key;
@@ -2683,7 +2706,7 @@
public V get(Object key) {
if (key == null) throw new NullPointerException();
K k = (K)key;
- return ((!inBounds(k)) ? null : m.get(k));
+ return (!inBounds(k)) ? null : m.get(k);
}
public V put(K key, V value) {
@@ -2850,35 +2873,35 @@
/* ---------------- Relational methods -------------- */
public Map.Entry<K,V> ceilingEntry(K key) {
- return getNearEntry(key, (m.GT|m.EQ));
+ return getNearEntry(key, GT|EQ);
}
public K ceilingKey(K key) {
- return getNearKey(key, (m.GT|m.EQ));
+ return getNearKey(key, GT|EQ);
}
public Map.Entry<K,V> lowerEntry(K key) {
- return getNearEntry(key, (m.LT));
+ return getNearEntry(key, LT);
}
public K lowerKey(K key) {
- return getNearKey(key, (m.LT));
+ return getNearKey(key, LT);
}
public Map.Entry<K,V> floorEntry(K key) {
- return getNearEntry(key, (m.LT|m.EQ));
+ return getNearEntry(key, LT|EQ);
}
public K floorKey(K key) {
- return getNearKey(key, (m.LT|m.EQ));
+ return getNearKey(key, LT|EQ);
}
public Map.Entry<K,V> higherEntry(K key) {
- return getNearEntry(key, (m.GT));
+ return getNearEntry(key, GT);
}
public K higherKey(K key) {
- return getNearKey(key, (m.GT));
+ return getNearKey(key, GT);
}
public K firstKey() {
@@ -2909,22 +2932,22 @@
public NavigableSet<K> keySet() {
KeySet<K> ks = keySetView;
- return (ks != null) ? ks : (keySetView = new KeySet(this));
+ return (ks != null) ? ks : (keySetView = new KeySet<K>(this));
}
public NavigableSet<K> navigableKeySet() {
KeySet<K> ks = keySetView;
- return (ks != null) ? ks : (keySetView = new KeySet(this));
+ return (ks != null) ? ks : (keySetView = new KeySet<K>(this));
}
public Collection<V> values() {
Collection<V> vs = valuesView;
- return (vs != null) ? vs : (valuesView = new Values(this));
+ return (vs != null) ? vs : (valuesView = new Values<V>(this));
}
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es = entrySetView;
- return (es != null) ? es : (entrySetView = new EntrySet(this));
+ return (es != null) ? es : (entrySetView = new EntrySet<K,V>(this));
}
public NavigableSet<K> descendingKeySet() {
@@ -3053,20 +3076,16 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", ConcurrentSkipListMap.class);
-
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ static {
try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ConcurrentSkipListMap.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
-
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
index d24876f..71431a9 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
@@ -1,12 +1,15 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.*;
-import sun.misc.Unsafe;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* A scalable concurrent {@link NavigableSet} implementation based on
@@ -29,12 +32,14 @@
* <p>Beware that, unlike in most collections, the <tt>size</tt>
* method is <em>not</em> a constant-time operation. Because of the
* asynchronous nature of these sets, determining the current number
- * of elements requires a traversal of the elements. Additionally, the
- * bulk operations <tt>addAll</tt>, <tt>removeAll</tt>,
- * <tt>retainAll</tt>, and <tt>containsAll</tt> are <em>not</em>
- * guaranteed to be performed atomically. For example, an iterator
- * operating concurrently with an <tt>addAll</tt> operation might view
- * only some of the added elements.
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations <tt>addAll</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, <tt>containsAll</tt>,
+ * <tt>equals</tt>, and <tt>toArray</tt> are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an <tt>addAll</tt> operation might view only some
+ * of the added elements.
*
* <p>This class and its iterators implement all of the
* <em>optional</em> methods of the {@link Set} and {@link Iterator}
@@ -43,10 +48,6 @@
* because <tt>null</tt> arguments and return values cannot be reliably
* distinguished from the absence of elements.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <E> the type of elements maintained by this set
* @since 1.6
@@ -127,15 +128,15 @@
* @return a shallow copy of this set
*/
public ConcurrentSkipListSet<E> clone() {
- ConcurrentSkipListSet<E> clone = null;
try {
- clone = (ConcurrentSkipListSet<E>) super.clone();
- clone.setMap(new ConcurrentSkipListMap(m));
+ @SuppressWarnings("unchecked")
+ ConcurrentSkipListSet<E> clone =
+ (ConcurrentSkipListSet<E>) super.clone();
+ clone.setMap(new ConcurrentSkipListMap<E,Object>(m));
+ return clone;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
-
- return clone;
}
/* ---------------- Set operations -------------- */
@@ -291,8 +292,8 @@
public boolean removeAll(Collection<?> c) {
// Override AbstractSet version to avoid unnecessary call to size()
boolean modified = false;
- for (Iterator<?> i = c.iterator(); i.hasNext(); )
- if (remove(i.next()))
+ for (Object e : c)
+ if (remove(e))
modified = true;
return modified;
}
@@ -437,20 +438,24 @@
* @return a reverse order view of this set
*/
public NavigableSet<E> descendingSet() {
- return new ConcurrentSkipListSet(m.descendingMap());
+ return new ConcurrentSkipListSet<E>(m.descendingMap());
}
// Support for resetting map in clone
- private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private void setMap(ConcurrentNavigableMap<E,Object> map) {
+ UNSAFE.putObjectVolatile(this, mapOffset, map);
+ }
+
+ private static final sun.misc.Unsafe UNSAFE;
private static final long mapOffset;
static {
try {
- mapOffset = unsafe.objectFieldOffset
- (ConcurrentSkipListSet.class.getDeclaredField("m"));
- } catch (Exception ex) { throw new Error(ex); }
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ConcurrentSkipListSet.class;
+ mapOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("m"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
}
- private void setMap(ConcurrentNavigableMap<E,Object> map) {
- unsafe.putObjectVolatile(this, mapOffset, map);
- }
-
}
diff --git a/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java b/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
index 87419fc..1f37bc9 100644
--- a/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
+++ b/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -160,8 +160,7 @@
* The following code can be used to dump the set into a newly allocated
* array of <tt>String</tt>:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
@@ -358,6 +357,6 @@
* Test for equality, coping with nulls.
*/
private static boolean eq(Object o1, Object o2) {
- return (o1 == null ? o2 == null : o1.equals(o2));
+ return (o1 == null) ? o2 == null : o1.equals(o2);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/CountDownLatch.java b/luni/src/main/java/java/util/concurrent/CountDownLatch.java
index 1888279..e90badf 100644
--- a/luni/src/main/java/java/util/concurrent/CountDownLatch.java
+++ b/luni/src/main/java/java/util/concurrent/CountDownLatch.java
@@ -1,12 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.concurrent.locks.*;
-import java.util.concurrent.atomic.*;
/**
* A synchronization aid that allows one or more threads to wait until
@@ -44,7 +43,7 @@
* until all workers have completed.
* </ul>
*
- * <pre>
+ * <pre> {@code
* class Driver { // ...
* void main() throws InterruptedException {
* CountDownLatch startSignal = new CountDownLatch(1);
@@ -76,9 +75,7 @@
* }
*
* void doWork() { ... }
- * }
- *
- * </pre>
+ * }}</pre>
*
* <p>Another typical usage would be to divide a problem into N parts,
* describe each part with a Runnable that executes that portion and
@@ -87,7 +84,7 @@
* will be able to pass through await. (When threads must repeatedly
* count down in this way, instead use a {@link CyclicBarrier}.)
*
- * <pre>
+ * <pre> {@code
* class Driver2 { // ...
* void main() throws InterruptedException {
* CountDownLatch doneSignal = new CountDownLatch(N);
@@ -115,9 +112,7 @@
* }
*
* void doWork() { ... }
- * }
- *
- * </pre>
+ * }}</pre>
*
* <p>Memory consistency effects: Until the count reaches
* zero, actions in a thread prior to calling
diff --git a/luni/src/main/java/java/util/concurrent/CyclicBarrier.java b/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
index d5738c5..cf0b46e 100644
--- a/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
+++ b/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -23,7 +23,8 @@
*
* <p><b>Sample usage:</b> Here is an example of
* using a barrier in a parallel decomposition design:
- * <pre>
+ *
+ * <pre> {@code
* class Solver {
* final int N;
* final float[][] data;
@@ -61,8 +62,8 @@
*
* waitUntilDone();
* }
- * }
- * </pre>
+ * }}</pre>
+ *
* Here, each worker thread processes a row of the matrix then waits at the
* barrier until all rows have been processed. When all rows are processed
* the supplied {@link Runnable} barrier action is executed and merges the
@@ -76,9 +77,10 @@
* {@link #await} returns the arrival index of that thread at the barrier.
* You can then choose which thread should execute the barrier action, for
* example:
- * <pre> if (barrier.await() == 0) {
- * // log the completion of this iteration
- * }</pre>
+ * <pre> {@code
+ * if (barrier.await() == 0) {
+ * // log the completion of this iteration
+ * }}</pre>
*
* <p>The <tt>CyclicBarrier</tt> uses an all-or-none breakage model
* for failed synchronization attempts: If a thread leaves a barrier
@@ -175,21 +177,21 @@
throw new InterruptedException();
}
- int index = --count;
- if (index == 0) { // tripped
- boolean ranAction = false;
- try {
- final Runnable command = barrierCommand;
- if (command != null)
- command.run();
- ranAction = true;
- nextGeneration();
- return 0;
- } finally {
- if (!ranAction)
- breakBarrier();
- }
- }
+ int index = --count;
+ if (index == 0) { // tripped
+ boolean ranAction = false;
+ try {
+ final Runnable command = barrierCommand;
+ if (command != null)
+ command.run();
+ ranAction = true;
+ nextGeneration();
+ return 0;
+ } finally {
+ if (!ranAction)
+ breakBarrier();
+ }
+ }
// loop until tripped, broken, interrupted, or timed out
for (;;) {
@@ -325,7 +327,7 @@
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
- throw new Error(toe); // cannot happen;
+ throw new Error(toe); // cannot happen
}
}
diff --git a/luni/src/main/java/java/util/concurrent/DelayQueue.java b/luni/src/main/java/java/util/concurrent/DelayQueue.java
index 8c44e82..52028cb 100644
--- a/luni/src/main/java/java/util/concurrent/DelayQueue.java
+++ b/luni/src/main/java/java/util/concurrent/DelayQueue.java
@@ -1,12 +1,14 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.locks.*;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
// BEGIN android-note
@@ -29,7 +31,9 @@
*
* <p>This class and its iterator implement all of the
* <em>optional</em> methods of the {@link Collection} and {@link
- * Iterator} interfaces.
+ * Iterator} interfaces. The Iterator provided in method {@link
+ * #iterator()} is <em>not</em> guaranteed to traverse the elements of
+ * the DelayQueue in any particular order.
*
* @since 1.5
* @author Doug Lea
@@ -154,7 +158,7 @@
lock.lock();
try {
E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
+ if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
@@ -179,7 +183,7 @@
if (first == null)
available.await();
else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
else if (leader != null)
@@ -226,7 +230,7 @@
else
nanos = available.awaitNanos(nanos);
} else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
if (nanos <= 0)
@@ -284,6 +288,17 @@
}
/**
+ * Return first element only if it is expired.
+ * Used only by drainTo. Call only when holding lock.
+ */
+ private E peekExpired() {
+ // assert lock.isHeldByCurrentThread();
+ E first = q.peek();
+ return (first == null || first.getDelay(NANOSECONDS) > 0) ?
+ null : first;
+ }
+
+ /**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
@@ -298,11 +313,9 @@
lock.lock();
try {
int n = 0;
- for (;;) {
- E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- break;
- c.add(q.poll());
+ for (E e; (e = peekExpired()) != null;) {
+ c.add(e); // In this order, in case add() throws.
+ q.poll();
++n;
}
return n;
@@ -328,11 +341,9 @@
lock.lock();
try {
int n = 0;
- while (n < maxElements) {
- E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- break;
- c.add(q.poll());
+ for (E e; n < maxElements && (e = peekExpired()) != null;) {
+ c.add(e); // In this order, in case add() throws.
+ q.poll();
++n;
}
return n;
@@ -411,8 +422,7 @@
* <p>The following code can be used to dump a delay queue into a newly
* allocated array of <tt>Delayed</tt>:
*
- * <pre>
- * Delayed[] a = q.toArray(new Delayed[0]);</pre>
+ * <pre> {@code Delayed[] a = q.toArray(new Delayed[0]);}</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
@@ -451,6 +461,24 @@
}
/**
+ * Identity-based version for use in Itr.remove
+ */
+ void removeEQ(Object o) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ for (Iterator<E> it = q.iterator(); it.hasNext(); ) {
+ if (o == it.next()) {
+ it.remove();
+ break;
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
* Returns an iterator over all the elements (both expired and
* unexpired) in this queue. The iterator does not return the
* elements in any particular order.
@@ -473,7 +501,7 @@
*/
private class Itr implements Iterator<E> {
final Object[] array; // Array of all elements
- int cursor; // index of next element to return;
+ int cursor; // index of next element to return
int lastRet; // index of last element, or -1 if no such
Itr(Object[] array) {
@@ -496,21 +524,8 @@
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
- Object x = array[lastRet];
+ removeEQ(array[lastRet]);
lastRet = -1;
- // Traverse underlying queue to find == element,
- // not just a .equals element.
- lock.lock();
- try {
- for (Iterator it = q.iterator(); it.hasNext(); ) {
- if (it.next() == x) {
- it.remove();
- return;
- }
- }
- } finally {
- lock.unlock();
- }
}
}
diff --git a/luni/src/main/java/java/util/concurrent/Delayed.java b/luni/src/main/java/java/util/concurrent/Delayed.java
index b1ff4ee..39d927c 100644
--- a/luni/src/main/java/java/util/concurrent/Delayed.java
+++ b/luni/src/main/java/java/util/concurrent/Delayed.java
@@ -1,13 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.*;
-
/**
* A mix-in style interface for marking objects that should be
* acted upon after a given delay.
diff --git a/luni/src/main/java/java/util/concurrent/Exchanger.java b/luni/src/main/java/java/util/concurrent/Exchanger.java
index 3c230be..6069dce 100644
--- a/luni/src/main/java/java/util/concurrent/Exchanger.java
+++ b/luni/src/main/java/java/util/concurrent/Exchanger.java
@@ -2,7 +2,7 @@
* Written by Doug Lea, Bill Scherer, and Michael Scott with
* assistance from members of JCP JSR-166 Expert Group and released to
* the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -23,7 +23,7 @@
* to swap buffers between threads so that the thread filling the
* buffer gets a freshly emptied one when it needs it, handing off the
* filled one to the thread emptying the buffer.
- * <pre>{@code
+ * <pre> {@code
* class FillAndEmpty {
* Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
* DataBuffer initialEmptyBuffer = ... a made-up type
@@ -59,8 +59,7 @@
* new Thread(new FillingLoop()).start();
* new Thread(new EmptyingLoop()).start();
* }
- * }
- * }</pre>
+ * }}</pre>
*
* <p>Memory consistency effects: For each pair of threads that
* successfully exchange objects via an {@code Exchanger}, actions
@@ -135,8 +134,8 @@
* races between two threads or thread pre-emptions occurring
* between reading and CASing. Also, very transient peak
* contention can be much higher than the average sustainable
- * levels. The max limit is decreased on average 50% of the times
- * that a non-slot-zero wait elapses without being fulfilled.
+ * levels. An attempt to decrease the max limit is usually made
+ * when a non-slot-zero wait elapses without being fulfilled.
* Threads experiencing elapsed waits move closer to zero, so
* eventually find existing (or future) threads even if the table
* has been shrunk due to inactivity. The chosen mechanics and
@@ -275,7 +274,9 @@
* extra space.
*/
private static final class Slot extends AtomicReference<Object> {
- // Improve likelihood of isolation on <= 64 byte cache lines
+ // Improve likelihood of isolation on <= 128 byte cache lines.
+ // We used to target 64 byte cache lines, but some x86s (including
+ // i7 under some BIOSes) actually use 128 byte cache lines.
long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe;
}
diff --git a/luni/src/main/java/java/util/concurrent/ExecutionException.java b/luni/src/main/java/java/util/concurrent/ExecutionException.java
index bc561e5..9bb8dee 100644
--- a/luni/src/main/java/java/util/concurrent/ExecutionException.java
+++ b/luni/src/main/java/java/util/concurrent/ExecutionException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -50,11 +50,9 @@
/**
* Constructs an <tt>ExecutionException</tt> with the specified cause.
- * The detail message is set to:
- * <pre>
- * (cause == null ? null : cause.toString())</pre>
- * (which typically contains the class and detail message of
- * <tt>cause</tt>).
+ * The detail message is set to {@code (cause == null ? null :
+ * cause.toString())} (which typically contains the class and
+ * detail message of <tt>cause</tt>).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method)
diff --git a/luni/src/main/java/java/util/concurrent/Executor.java b/luni/src/main/java/java/util/concurrent/Executor.java
index fbc4e6f..831bf46 100644
--- a/luni/src/main/java/java/util/concurrent/Executor.java
+++ b/luni/src/main/java/java/util/concurrent/Executor.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -27,23 +27,23 @@
* executor can run the submitted task immediately in the caller's
* thread:
*
- * <pre>
+ * <pre> {@code
* class DirectExecutor implements Executor {
- * public void execute(Runnable r) {
- * r.run();
- * }
- * }</pre>
+ * public void execute(Runnable r) {
+ * r.run();
+ * }
+ * }}</pre>
*
* More typically, tasks are executed in some thread other
* than the caller's thread. The executor below spawns a new thread
* for each task.
*
- * <pre>
+ * <pre> {@code
* class ThreadPerTaskExecutor implements Executor {
- * public void execute(Runnable r) {
- * new Thread(r).start();
- * }
- * }</pre>
+ * public void execute(Runnable r) {
+ * new Thread(r).start();
+ * }
+ * }}</pre>
*
* Many <tt>Executor</tt> implementations impose some sort of
* limitation on how and when tasks are scheduled. The executor below
diff --git a/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
index b41955d..c0d6006 100644
--- a/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
+++ b/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ExecutorService.java b/luni/src/main/java/java/util/concurrent/ExecutorService.java
index 89688e4..a33ceec 100644
--- a/luni/src/main/java/java/util/concurrent/ExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/ExecutorService.java
@@ -1,14 +1,16 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.List;
import java.util.Collection;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
+
+// BEGIN android-note
+// removed security manager docs
+// END android-note
/**
* An {@link Executor} that provides methods to manage termination and
@@ -44,7 +46,7 @@
* pool service incoming requests. It uses the preconfigured {@link
* Executors#newFixedThreadPool} factory method:
*
- * <pre>
+ * <pre> {@code
* class NetworkService implements Runnable {
* private final ServerSocket serverSocket;
* private final ExecutorService pool;
@@ -72,14 +74,13 @@
* public void run() {
* // read and service request on socket
* }
- * }
- * </pre>
+ * }}</pre>
*
* The following method shuts down an <tt>ExecutorService</tt> in two phases,
* first by calling <tt>shutdown</tt> to reject incoming tasks, and then
* calling <tt>shutdownNow</tt>, if necessary, to cancel any lingering tasks:
*
- * <pre>
+ * <pre> {@code
* void shutdownAndAwaitTermination(ExecutorService pool) {
* pool.shutdown(); // Disable new tasks from being submitted
* try {
@@ -96,8 +97,7 @@
* // Preserve interrupt status
* Thread.currentThread().interrupt();
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>Memory consistency effects: Actions in a thread prior to the
* submission of a {@code Runnable} or {@code Callable} task to an
diff --git a/luni/src/main/java/java/util/concurrent/Executors.java b/luni/src/main/java/java/util/concurrent/Executors.java
index e42b0cc..b4f03ba 100644
--- a/luni/src/main/java/java/util/concurrent/Executors.java
+++ b/luni/src/main/java/java/util/concurrent/Executors.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -12,9 +12,10 @@
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
-import java.security.AccessControlException;
-// import sun.security.util.SecurityConstants; // android-removed
+// BEGIN android-note
+// removed security manager docs
+// END android-note
/**
* Factory and utility methods for {@link Executor}, {@link
* ExecutorService}, {@link ScheduledExecutorService}, {@link
@@ -271,10 +272,7 @@
/**
* Returns a default thread factory used to create new threads.
* This factory creates all new threads used by an Executor in the
- * same {@link ThreadGroup}. If there is a {@link
- * java.lang.SecurityManager}, it uses the group of {@link
- * System#getSecurityManager}, else the group of the thread
- * invoking this <tt>defaultThreadFactory</tt> method. Each new
+ * same {@link ThreadGroup}. Each new
* thread is created as a non-daemon thread with priority set to
* the smaller of <tt>Thread.NORM_PRIORITY</tt> and the maximum
* priority permitted in the thread group. New threads have names
@@ -289,36 +287,7 @@
}
/**
- * Returns a thread factory used to create new threads that
- * have the same permissions as the current thread.
- * This factory creates threads with the same settings as {@link
- * Executors#defaultThreadFactory}, additionally setting the
- * AccessControlContext and contextClassLoader of new threads to
- * be the same as the thread invoking this
- * <tt>privilegedThreadFactory</tt> method. A new
- * <tt>privilegedThreadFactory</tt> can be created within an
- * {@link AccessController#doPrivileged} action setting the
- * current thread's access control context to create threads with
- * the selected permission settings holding within that action.
- *
- * <p> Note that while tasks running within such threads will have
- * the same access control and class loader settings as the
- * current thread, they need not have the same {@link
- * java.lang.ThreadLocal} or {@link
- * java.lang.InheritableThreadLocal} values. If necessary,
- * particular values of thread locals can be set or reset before
- * any task runs in {@link ThreadPoolExecutor} subclasses using
- * {@link ThreadPoolExecutor#beforeExecute}. Also, if it is
- * necessary to initialize worker threads to have the same
- * InheritableThreadLocal settings as some other designated
- * thread, you can create a custom ThreadFactory in which that
- * thread waits for and services requests to create others that
- * will inherit its values.
- *
- * @return a thread factory
- * @throws AccessControlException if the current access control
- * context does not have permission to both get and set context
- * class loader.
+ * Legacy security code; do not use.
*/
public static ThreadFactory privilegedThreadFactory() {
return new PrivilegedThreadFactory();
@@ -383,18 +352,7 @@
}
/**
- * Returns a {@link Callable} object that will, when
- * called, execute the given <tt>callable</tt> under the current
- * access control context. This method should normally be
- * invoked within an {@link AccessController#doPrivileged} action
- * to create callables that will, if possible, execute under the
- * selected permission settings holding within that action; or if
- * not possible, throw an associated {@link
- * AccessControlException}.
- * @param callable the underlying task
- * @return a callable object
- * @throws NullPointerException if callable null
- *
+ * Legacy security code; do not use.
*/
public static <T> Callable<T> privilegedCallable(Callable<T> callable) {
if (callable == null)
@@ -405,20 +363,10 @@
/**
* Returns a {@link Callable} object that will, when
* called, execute the given <tt>callable</tt> under the current
- * access control context, with the current context class loader
- * as the context class loader. This method should normally be
- * invoked within an {@link AccessController#doPrivileged} action
- * to create callables that will, if possible, execute under the
- * selected permission settings holding within that action; or if
- * not possible, throw an associated {@link
- * AccessControlException}.
- * @param callable the underlying task
+ * with the current context class loader as the context class loader.
*
* @return a callable object
* @throws NullPointerException if callable null
- * @throws AccessControlException if the current access control
- * context does not have permission to both set and get context
- * class loader.
*/
public static <T> Callable<T> privilegedCallableUsingCurrentClassLoader(Callable<T> callable) {
if (callable == null)
@@ -480,17 +428,19 @@
private final ClassLoader ccl;
PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- // Calls to getContextClassLoader from this class
- // never trigger a security check, but we check
- // whether our callers have this permission anyways.
- sm.checkPermission(new RuntimePermission("getContextClassLoader")); // android-changed
-
- // Whether setContextClassLoader turns out to be necessary
- // or not, we fail fast if permission is not available.
- sm.checkPermission(new RuntimePermission("setContextClassLoader"));
- }
+ // BEGIN android-removed
+ // SecurityManager sm = System.getSecurityManager();
+ // if (sm != null) {
+ // // Calls to getContextClassLoader from this class
+ // // never trigger a security check, but we check
+ // // whether our callers have this permission anyways.
+ // sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
+ //
+ // // Whether setContextClassLoader turns out to be necessary
+ // // or not, we fail fast if permission is not available.
+ // sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+ // }
+ // END android-removed
this.task = task;
this.acc = AccessController.getContext();
this.ccl = Thread.currentThread().getContextClassLoader();
@@ -561,16 +511,18 @@
PrivilegedThreadFactory() {
super();
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- // Calls to getContextClassLoader from this class
- // never trigger a security check, but we check
- // whether our callers have this permission anyways.
- sm.checkPermission(new RuntimePermission("getContextClassLoader")); // android-changed
-
- // Fail fast
- sm.checkPermission(new RuntimePermission("setContextClassLoader"));
- }
+ // BEGIN android-removed
+ // SecurityManager sm = System.getSecurityManager();
+ // if (sm != null) {
+ // // Calls to getContextClassLoader from this class
+ // // never trigger a security check, but we check
+ // // whether our callers have this permission anyways.
+ // sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
+ //
+ // // Fail fast
+ // sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+ // }
+ // END android-removed
this.acc = AccessController.getContext();
this.ccl = Thread.currentThread().getContextClassLoader();
}
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
new file mode 100644
index 0000000..ee15ac8
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
@@ -0,0 +1,2127 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.LockSupport;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.Condition;
+import libcore.util.SneakyThrow;
+
+// BEGIN android-note
+// removed security manager docs
+// END android-note
+
+/**
+ * An {@link ExecutorService} for running {@link ForkJoinTask}s.
+ * A {@code ForkJoinPool} provides the entry point for submissions
+ * from non-{@code ForkJoinTask} clients, as well as management and
+ * monitoring operations.
+ *
+ * <p>A {@code ForkJoinPool} differs from other kinds of {@link
+ * ExecutorService} mainly by virtue of employing
+ * <em>work-stealing</em>: all threads in the pool attempt to find and
+ * execute subtasks created by other active tasks (eventually blocking
+ * waiting for work if none exist). This enables efficient processing
+ * when most tasks spawn other subtasks (as do most {@code
+ * ForkJoinTask}s). When setting <em>asyncMode</em> to true in
+ * constructors, {@code ForkJoinPool}s may also be appropriate for use
+ * with event-style tasks that are never joined.
+ *
+ * <p>A {@code ForkJoinPool} is constructed with a given target
+ * parallelism level; by default, equal to the number of available
+ * processors. The pool attempts to maintain enough active (or
+ * available) threads by dynamically adding, suspending, or resuming
+ * internal worker threads, even if some tasks are stalled waiting to
+ * join others. However, no such adjustments are guaranteed in the
+ * face of blocked IO or other unmanaged synchronization. The nested
+ * {@link ManagedBlocker} interface enables extension of the kinds of
+ * synchronization accommodated.
+ *
+ * <p>In addition to execution and lifecycle control methods, this
+ * class provides status check methods (for example
+ * {@link #getStealCount}) that are intended to aid in developing,
+ * tuning, and monitoring fork/join applications. Also, method
+ * {@link #toString} returns indications of pool state in a
+ * convenient form for informal monitoring.
+ *
+ * <p> As is the case with other ExecutorServices, there are three
+ * main task execution methods summarized in the following
+ * table. These are designed to be used by clients not already engaged
+ * in fork/join computations in the current pool. The main forms of
+ * these methods accept instances of {@code ForkJoinTask}, but
+ * overloaded forms also allow mixed execution of plain {@code
+ * Runnable}- or {@code Callable}- based activities as well. However,
+ * tasks that are already executing in a pool should normally
+ * <em>NOT</em> use these pool execution methods, but instead use the
+ * within-computation forms listed in the table.
+ *
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <tr>
+ * <td></td>
+ * <td ALIGN=CENTER> <b>Call from non-fork/join clients</b></td>
+ * <td ALIGN=CENTER> <b>Call from within fork/join computations</b></td>
+ * </tr>
+ * <tr>
+ * <td> <b>Arrange async execution</td>
+ * <td> {@link #execute(ForkJoinTask)}</td>
+ * <td> {@link ForkJoinTask#fork}</td>
+ * </tr>
+ * <tr>
+ * <td> <b>Await and obtain result</td>
+ * <td> {@link #invoke(ForkJoinTask)}</td>
+ * <td> {@link ForkJoinTask#invoke}</td>
+ * </tr>
+ * <tr>
+ * <td> <b>Arrange exec and obtain Future</td>
+ * <td> {@link #submit(ForkJoinTask)}</td>
+ * <td> {@link ForkJoinTask#fork} (ForkJoinTasks <em>are</em> Futures)</td>
+ * </tr>
+ * </table>
+ *
+ * <p><b>Sample Usage.</b> Normally a single {@code ForkJoinPool} is
+ * used for all parallel task execution in a program or subsystem.
+ * Otherwise, use would not usually outweigh the construction and
+ * bookkeeping overhead of creating a large set of threads. For
+ * example, a common pool could be used for the {@code SortTasks}
+ * illustrated in {@link RecursiveAction}. Because {@code
+ * ForkJoinPool} uses threads in {@linkplain java.lang.Thread#isDaemon
+ * daemon} mode, there is typically no need to explicitly {@link
+ * #shutdown} such a pool upon program exit.
+ *
+ * <pre> {@code
+ * static final ForkJoinPool mainPool = new ForkJoinPool();
+ * ...
+ * public void sort(long[] array) {
+ * mainPool.invoke(new SortTask(array, 0, array.length));
+ * }}</pre>
+ *
+ * <p><b>Implementation notes</b>: This implementation restricts the
+ * maximum number of running threads to 32767. Attempts to create
+ * pools with greater than the maximum number result in
+ * {@code IllegalArgumentException}.
+ *
+ * <p>This implementation rejects submitted tasks (that is, by throwing
+ * {@link RejectedExecutionException}) only when the pool is shut down
+ * or internal resources have been exhausted.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class ForkJoinPool extends AbstractExecutorService {
+
+ /*
+ * Implementation Overview
+ *
+ * This class provides the central bookkeeping and control for a
+ * set of worker threads: Submissions from non-FJ threads enter
+ * into a submission queue. Workers take these tasks and typically
+ * split them into subtasks that may be stolen by other workers.
+ * Preference rules give first priority to processing tasks from
+ * their own queues (LIFO or FIFO, depending on mode), then to
+ * randomized FIFO steals of tasks in other worker queues, and
+ * lastly to new submissions.
+ *
+ * The main throughput advantages of work-stealing stem from
+ * decentralized control -- workers mostly take tasks from
+ * themselves or each other. We cannot negate this in the
+ * implementation of other management responsibilities. The main
+ * tactic for avoiding bottlenecks is packing nearly all
+ * essentially atomic control state into a single 64bit volatile
+ * variable ("ctl"). This variable is read on the order of 10-100
+ * times as often as it is modified (always via CAS). (There is
+ * some additional control state, for example variable "shutdown"
+ * for which we can cope with uncoordinated updates.) This
+ * streamlines synchronization and control at the expense of messy
+ * constructions needed to repack status bits upon updates.
+ * Updates tend not to contend with each other except during
+ * bursts while submitted tasks begin or end. In some cases when
+ * they do contend, threads can instead do something else
+ * (usually, scan for tasks) until contention subsides.
+ *
+ * To enable packing, we restrict maximum parallelism to (1<<15)-1
+ * (which is far in excess of normal operating range) to allow
+ * ids, counts, and their negations (used for thresholding) to fit
+ * into 16bit fields.
+ *
+ * Recording Workers. Workers are recorded in the "workers" array
+ * that is created upon pool construction and expanded if (rarely)
+ * necessary. This is an array as opposed to some other data
+ * structure to support index-based random steals by workers.
+ * Updates to the array recording new workers and unrecording
+ * terminated ones are protected from each other by a seqLock
+ * (scanGuard) but the array is otherwise concurrently readable,
+ * and accessed directly by workers. To simplify index-based
+ * operations, the array size is always a power of two, and all
+ * readers must tolerate null slots. To avoid flailing during
+ * start-up, the array is presized to hold twice #parallelism
+ * workers (which is unlikely to need further resizing during
+ * execution). But to avoid dealing with so many null slots,
+ * variable scanGuard includes a mask for the nearest power of two
+ * that contains all current workers. All worker thread creation
+ * is on-demand, triggered by task submissions, replacement of
+ * terminated workers, and/or compensation for blocked
+ * workers. However, all other support code is set up to work with
+ * other policies. To ensure that we do not hold on to worker
+ * references that would prevent GC, ALL accesses to workers are
+ * via indices into the workers array (which is one source of some
+ * of the messy code constructions here). In essence, the workers
+ * array serves as a weak reference mechanism. Thus for example
+ * the wait queue field of ctl stores worker indices, not worker
+ * references. Access to the workers in associated methods (for
+ * example signalWork) must both index-check and null-check the
+ * IDs. All such accesses ignore bad IDs by returning out early
+ * from what they are doing, since this can only be associated
+ * with termination, in which case it is OK to give up.
+ *
+ * All uses of the workers array, as well as queue arrays, check
+ * that the array is non-null (even if previously non-null). This
+ * allows nulling during termination, which is currently not
+ * necessary, but remains an option for resource-revocation-based
+ * shutdown schemes.
+ *
+ * Wait Queuing. Unlike HPC work-stealing frameworks, we cannot
+ * let workers spin indefinitely scanning for tasks when none can
+ * be found immediately, and we cannot start/resume workers unless
+ * there appear to be tasks available. On the other hand, we must
+ * quickly prod them into action when new tasks are submitted or
+ * generated. We park/unpark workers after placing in an event
+ * wait queue when they cannot find work. This "queue" is actually
+ * a simple Treiber stack, headed by the "id" field of ctl, plus a
+ * 15bit counter value to both wake up waiters (by advancing their
+ * count) and avoid ABA effects. Successors are held in worker
+ * field "nextWait". Queuing deals with several intrinsic races,
+ * mainly that a task-producing thread can miss seeing (and
+ * signalling) another thread that gave up looking for work but
+ * has not yet entered the wait queue. We solve this by requiring
+ * a full sweep of all workers both before (in scan()) and after
+ * (in tryAwaitWork()) a newly waiting worker is added to the wait
+ * queue. During a rescan, the worker might release some other
+ * queued worker rather than itself, which has the same net
+ * effect. Because enqueued workers may actually be rescanning
+ * rather than waiting, we set and clear the "parked" field of
+ * ForkJoinWorkerThread to reduce unnecessary calls to unpark.
+ * (Use of the parked field requires a secondary recheck to avoid
+ * missed signals.)
+ *
+ * Signalling. We create or wake up workers only when there
+ * appears to be at least one task they might be able to find and
+ * execute. When a submission is added or another worker adds a
+ * task to a queue that previously had two or fewer tasks, they
+ * signal waiting workers (or trigger creation of new ones if
+ * fewer than the given parallelism level -- see signalWork).
+ * These primary signals are buttressed by signals during rescans
+ * as well as those performed when a worker steals a task and
+ * notices that there are more tasks too; together these cover the
+ * signals needed in cases when more than two tasks are pushed
+ * but untaken.
+ *
+ * Trimming workers. To release resources after periods of lack of
+ * use, a worker starting to wait when the pool is quiescent will
+ * time out and terminate if the pool has remained quiescent for
+ * SHRINK_RATE nanosecs. This will slowly propagate, eventually
+ * terminating all workers after long periods of non-use.
+ *
+ * Submissions. External submissions are maintained in an
+ * array-based queue that is structured identically to
+ * ForkJoinWorkerThread queues except for the use of
+ * submissionLock in method addSubmission. Unlike the case for
+ * worker queues, multiple external threads can add new
+ * submissions, so adding requires a lock.
+ *
+ * Compensation. Beyond work-stealing support and lifecycle
+ * control, the main responsibility of this framework is to take
+ * actions when one worker is waiting to join a task stolen (or
+ * always held by) another. Because we are multiplexing many
+ * tasks on to a pool of workers, we can't just let them block (as
+ * in Thread.join). We also cannot just reassign the joiner's
+ * run-time stack with another and replace it later, which would
+ * be a form of "continuation", that even if possible is not
+ * necessarily a good idea since we sometimes need both an
+ * unblocked task and its continuation to progress. Instead we
+ * combine two tactics:
+ *
+ * Helping: Arranging for the joiner to execute some task that it
+ * would be running if the steal had not occurred. Method
+ * ForkJoinWorkerThread.joinTask tracks joining->stealing
+ * links to try to find such a task.
+ *
+ * Compensating: Unless there are already enough live threads,
+ * method tryPreBlock() may create or re-activate a spare
+ * thread to compensate for blocked joiners until they
+ * unblock.
+ *
+ * The ManagedBlocker extension API can't use helping so relies
+ * only on compensation in method awaitBlocker.
+ *
+ * It is impossible to keep exactly the target parallelism number
+ * of threads running at any given time. Determining the
+ * existence of conservatively safe helping targets, the
+ * availability of already-created spares, and the apparent need
+ * to create new spares are all racy and require heuristic
+ * guidance, so we rely on multiple retries of each. Currently,
+ * in keeping with on-demand signalling policy, we compensate only
+ * if blocking would leave less than one active (non-waiting,
+ * non-blocked) worker. Additionally, to avoid some false alarms
+ * due to GC, lagging counters, system activity, etc, compensated
+ * blocking for joins is only attempted after rechecks stabilize
+ * (retries are interspersed with Thread.yield, for good
+ * citizenship). The variable blockedCount, incremented before
+ * blocking and decremented after, is sometimes needed to
+ * distinguish cases of waiting for work vs blocking on joins or
+ * other managed sync. Both cases are equivalent for most pool
+ * control, so we can update non-atomically. (Additionally,
+ * contention on blockedCount alleviates some contention on ctl).
+ *
+ * Shutdown and Termination. A call to shutdownNow atomically sets
+ * the ctl stop bit and then (non-atomically) sets each workers
+ * "terminate" status, cancels all unprocessed tasks, and wakes up
+ * all waiting workers. Detecting whether termination should
+ * commence after a non-abrupt shutdown() call requires more work
+ * and bookkeeping. We need consensus about quiescence (i.e., that
+ * there is no more work) which is reflected in active counts so
+ * long as there are no current blockers, as well as possible
+ * re-evaluations during independent changes in blocking or
+ * quiescing workers.
+ *
+ * Style notes: There is a lot of representation-level coupling
+ * among classes ForkJoinPool, ForkJoinWorkerThread, and
+ * ForkJoinTask. Most fields of ForkJoinWorkerThread maintain
+ * data structures managed by ForkJoinPool, so are directly
+ * accessed. Conversely we allow access to "workers" array by
+ * workers, and direct access to ForkJoinTask.status by both
+ * ForkJoinPool and ForkJoinWorkerThread. There is little point
+ * trying to reduce this, since any associated future changes in
+ * representations will need to be accompanied by algorithmic
+ * changes anyway. All together, these low-level implementation
+ * choices produce as much as a factor of 4 performance
+ * improvement compared to naive implementations, and enable the
+ * processing of billions of tasks per second, at the expense of
+ * some ugliness.
+ *
+ * Methods signalWork() and scan() are the main bottlenecks so are
+ * especially heavily micro-optimized/mangled. There are lots of
+ * inline assignments (of form "while ((local = field) != 0)")
+ * which are usually the simplest way to ensure the required read
+ * orderings (which are sometimes critical). This leads to a
+ * "C"-like style of listing declarations of these locals at the
+ * heads of methods or blocks. There are several occurrences of
+ * the unusual "do {} while (!cas...)" which is the simplest way
+ * to force an update of a CAS'ed variable. There are also other
+ * coding oddities that help some methods perform reasonably even
+ * when interpreted (not compiled).
+ *
+ * The order of declarations in this file is: (1) declarations of
+ * statics (2) fields (along with constants used when unpacking
+ * some of them), listed in an order that tends to reduce
+ * contention among them a bit under most JVMs. (3) internal
+ * control methods (4) callbacks and other support for
+ * ForkJoinTask and ForkJoinWorkerThread classes, (5) exported
+ * methods (plus a few little helpers). (6) static block
+ * initializing all statics in a minimally dependent order.
+ */
+
+ /**
+ * Factory for creating new {@link ForkJoinWorkerThread}s.
+ * A {@code ForkJoinWorkerThreadFactory} must be defined and used
+ * for {@code ForkJoinWorkerThread} subclasses that extend base
+ * functionality or initialize threads with different contexts.
+ */
+ public static interface ForkJoinWorkerThreadFactory {
+ /**
+ * Returns a new worker thread operating in the given pool.
+ *
+ * @param pool the pool this thread works in
+ * @throws NullPointerException if the pool is null
+ */
+ public ForkJoinWorkerThread newThread(ForkJoinPool pool);
+ }
+
+ /**
+ * Default ForkJoinWorkerThreadFactory implementation; creates a
+ * new ForkJoinWorkerThread.
+ */
+ static class DefaultForkJoinWorkerThreadFactory
+ implements ForkJoinWorkerThreadFactory {
+ public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
+ return new ForkJoinWorkerThread(pool);
+ }
+ }
+
+ /**
+ * Creates a new ForkJoinWorkerThread. This factory is used unless
+ * overridden in ForkJoinPool constructors.
+ */
+ public static final ForkJoinWorkerThreadFactory
+ defaultForkJoinWorkerThreadFactory;
+
+ /**
+ * Permission required for callers of methods that may start or
+ * kill threads.
+ */
+ private static final RuntimePermission modifyThreadPermission;
+
+ /**
+ * If there is a security manager, makes sure caller has
+ * permission to modify threads.
+ */
+ private static void checkPermission() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null)
+ security.checkPermission(modifyThreadPermission);
+ }
+
+ /**
+ * Generator for assigning sequence numbers as pool names.
+ */
+ private static final AtomicInteger poolNumberGenerator;
+
+ /**
+ * Generator for initial random seeds for worker victim
+ * selection. This is used only to create initial seeds. Random
+ * steals use a cheaper xorshift generator per steal attempt. We
+ * don't expect much contention on seedGenerator, so just use a
+ * plain Random.
+ */
+ static final Random workerSeedGenerator;
+
+ /**
+ * Array holding all worker threads in the pool. Initialized upon
+ * construction. Array size must be a power of two. Updates and
+ * replacements are protected by scanGuard, but the array is
+ * always kept in a consistent enough state to be randomly
+ * accessed without locking by workers performing work-stealing,
+ * as well as other traversal-based methods in this class, so long
+ * as reads memory-acquire by first reading ctl. All readers must
+ * tolerate that some array slots may be null.
+ */
+ ForkJoinWorkerThread[] workers;
+
+ /**
+ * Initial size for submission queue array. Must be a power of
+ * two. In many applications, these always stay small so we use a
+ * small initial cap.
+ */
+ private static final int INITIAL_QUEUE_CAPACITY = 8;
+
+ /**
+ * Maximum size for submission queue array. Must be a power of two
+ * less than or equal to 1 << (31 - width of array entry) to
+ * ensure lack of index wraparound, but is capped at a lower
+ * value to help users trap runaway computations.
+ */
+ private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M
+
+ /**
+ * Array serving as submission queue. Initialized upon construction.
+ */
+ private ForkJoinTask<?>[] submissionQueue;
+
+ /**
+ * Lock protecting submissions array for addSubmission
+ */
+ private final ReentrantLock submissionLock;
+
+ /**
+ * Condition for awaitTermination, using submissionLock for
+ * convenience.
+ */
+ private final Condition termination;
+
+ /**
+ * Creation factory for worker threads.
+ */
+ private final ForkJoinWorkerThreadFactory factory;
+
+ /**
+ * The uncaught exception handler used when any worker abruptly
+ * terminates.
+ */
+ final Thread.UncaughtExceptionHandler ueh;
+
+ /**
+ * Prefix for assigning names to worker threads
+ */
+ private final String workerNamePrefix;
+
+ /**
+ * Sum of per-thread steal counts, updated only when threads are
+ * idle or terminating.
+ */
+ private volatile long stealCount;
+
+ /**
+ * Main pool control -- a long packed with:
+ * AC: Number of active running workers minus target parallelism (16 bits)
+ * TC: Number of total workers minus target parallelism (16 bits)
+ * ST: true if pool is terminating (1 bit)
+ * EC: the wait count of top waiting thread (15 bits)
+ * ID: ~poolIndex of top of Treiber stack of waiting threads (16 bits)
+ *
+ * When convenient, we can extract the upper 32 bits of counts and
+ * the lower 32 bits of queue state, u = (int)(ctl >>> 32) and e =
+ * (int)ctl. The ec field is never accessed alone, but always
+ * together with id and st. The offsets of counts by the target
+ * parallelism and the positionings of fields makes it possible to
+ * perform the most common checks via sign tests of fields: When
+ * ac is negative, there are not enough active workers, when tc is
+ * negative, there are not enough total workers, when id is
+ * negative, there is at least one waiting worker, and when e is
+ * negative, the pool is terminating. To deal with these possibly
+ * negative fields, we use casts in and out of "short" and/or
+ * signed shifts to maintain signedness.
+ */
+ volatile long ctl;
+
+ // bit positions/shifts for fields
+ private static final int AC_SHIFT = 48;
+ private static final int TC_SHIFT = 32;
+ private static final int ST_SHIFT = 31;
+ private static final int EC_SHIFT = 16;
+
+ // bounds
+ private static final int MAX_ID = 0x7fff; // max poolIndex
+ private static final int SMASK = 0xffff; // mask short bits
+ private static final int SHORT_SIGN = 1 << 15;
+ private static final int INT_SIGN = 1 << 31;
+
+ // masks
+ private static final long STOP_BIT = 0x0001L << ST_SHIFT;
+ private static final long AC_MASK = ((long)SMASK) << AC_SHIFT;
+ private static final long TC_MASK = ((long)SMASK) << TC_SHIFT;
+
+ // units for incrementing and decrementing
+ private static final long TC_UNIT = 1L << TC_SHIFT;
+ private static final long AC_UNIT = 1L << AC_SHIFT;
+
+ // masks and units for dealing with u = (int)(ctl >>> 32)
+ private static final int UAC_SHIFT = AC_SHIFT - 32;
+ private static final int UTC_SHIFT = TC_SHIFT - 32;
+ private static final int UAC_MASK = SMASK << UAC_SHIFT;
+ private static final int UTC_MASK = SMASK << UTC_SHIFT;
+ private static final int UAC_UNIT = 1 << UAC_SHIFT;
+ private static final int UTC_UNIT = 1 << UTC_SHIFT;
+
+ // masks and units for dealing with e = (int)ctl
+ private static final int E_MASK = 0x7fffffff; // no STOP_BIT
+ private static final int EC_UNIT = 1 << EC_SHIFT;
+
+ /**
+ * The target parallelism level.
+ */
+ final int parallelism;
+
+ /**
+ * Index (mod submission queue length) of next element to take
+ * from submission queue. Usage is identical to that for
+ * per-worker queues -- see ForkJoinWorkerThread internal
+ * documentation.
+ */
+ volatile int queueBase;
+
+ /**
+ * Index (mod submission queue length) of next element to add
+ * in submission queue. Usage is identical to that for
+ * per-worker queues -- see ForkJoinWorkerThread internal
+ * documentation.
+ */
+ int queueTop;
+
+ /**
+ * True when shutdown() has been called.
+ */
+ volatile boolean shutdown;
+
+ /**
+ * True if use local fifo, not default lifo, for local polling.
+ * Read by, and replicated by ForkJoinWorkerThreads.
+ */
+ final boolean locallyFifo;
+
+ /**
+ * The number of threads in ForkJoinWorkerThreads.helpQuiescePool.
+ * When non-zero, suppresses automatic shutdown when active
+ * counts become zero.
+ */
+ volatile int quiescerCount;
+
+ /**
+ * The number of threads blocked in join.
+ */
+ volatile int blockedCount;
+
+ /**
+ * Counter for worker Thread names (unrelated to their poolIndex)
+ */
+ private volatile int nextWorkerNumber;
+
+ /**
+ * The index for the next created worker. Accessed under scanGuard.
+ */
+ private int nextWorkerIndex;
+
+ /**
+ * SeqLock and index masking for updates to workers array. Locked
+ * when SG_UNIT is set. Unlocking clears bit by adding
+ * SG_UNIT. Staleness of read-only operations can be checked by
+ * comparing scanGuard to value before the reads. The low 16 bits
+ * (i.e, anding with SMASK) hold (the smallest power of two
+ * covering all worker indices, minus one, and is used to avoid
+ * dealing with large numbers of null slots when the workers array
+ * is overallocated.
+ */
+ volatile int scanGuard;
+
+ private static final int SG_UNIT = 1 << 16;
+
+ /**
+ * The wakeup interval (in nanoseconds) for a worker waiting for a
+ * task when the pool is quiescent to instead try to shrink the
+ * number of workers. The exact value does not matter too
+ * much. It must be short enough to release resources during
+ * sustained periods of idleness, but not so short that threads
+ * are continually re-created.
+ */
+ private static final long SHRINK_RATE =
+ 4L * 1000L * 1000L * 1000L; // 4 seconds
+
+ /**
+ * Top-level loop for worker threads: On each step: if the
+ * previous step swept through all queues and found no tasks, or
+ * there are excess threads, then possibly blocks. Otherwise,
+ * scans for and, if found, executes a task. Returns when pool
+ * and/or worker terminate.
+ *
+ * @param w the worker
+ */
+ final void work(ForkJoinWorkerThread w) {
+ boolean swept = false; // true on empty scans
+ long c;
+ while (!w.terminate && (int)(c = ctl) >= 0) {
+ int a; // active count
+ if (!swept && (a = (int)(c >> AC_SHIFT)) <= 0)
+ swept = scan(w, a);
+ else if (tryAwaitWork(w, c))
+ swept = false;
+ }
+ }
+
+ // Signalling
+
+ /**
+ * Wakes up or creates a worker.
+ */
+ final void signalWork() {
+ /*
+ * The while condition is true if: (there is are too few total
+ * workers OR there is at least one waiter) AND (there are too
+ * few active workers OR the pool is terminating). The value
+ * of e distinguishes the remaining cases: zero (no waiters)
+ * for create, negative if terminating (in which case do
+ * nothing), else release a waiter. The secondary checks for
+ * release (non-null array etc) can fail if the pool begins
+ * terminating after the test, and don't impose any added cost
+ * because JVMs must perform null and bounds checks anyway.
+ */
+ long c; int e, u;
+ while ((((e = (int)(c = ctl)) | (u = (int)(c >>> 32))) &
+ (INT_SIGN|SHORT_SIGN)) == (INT_SIGN|SHORT_SIGN) && e >= 0) {
+ if (e > 0) { // release a waiting worker
+ int i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws;
+ if ((ws = workers) == null ||
+ (i = ~e & SMASK) >= ws.length ||
+ (w = ws[i]) == null)
+ break;
+ long nc = (((long)(w.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (w.eventCount == e &&
+ UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
+ w.eventCount = (e + EC_UNIT) & E_MASK;
+ if (w.parked)
+ UNSAFE.unpark(w);
+ break;
+ }
+ }
+ else if (UNSAFE.compareAndSwapLong
+ (this, ctlOffset, c,
+ (long)(((u + UTC_UNIT) & UTC_MASK) |
+ ((u + UAC_UNIT) & UAC_MASK)) << 32)) {
+ addWorker();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Variant of signalWork to help release waiters on rescans.
+ * Tries once to release a waiter if active count < 0.
+ *
+ * @return false if failed due to contention, else true
+ */
+ private boolean tryReleaseWaiter() {
+ long c; int e, i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws;
+ if ((e = (int)(c = ctl)) > 0 &&
+ (int)(c >> AC_SHIFT) < 0 &&
+ (ws = workers) != null &&
+ (i = ~e & SMASK) < ws.length &&
+ (w = ws[i]) != null) {
+ long nc = ((long)(w.nextWait & E_MASK) |
+ ((c + AC_UNIT) & (AC_MASK|TC_MASK)));
+ if (w.eventCount != e ||
+ !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc))
+ return false;
+ w.eventCount = (e + EC_UNIT) & E_MASK;
+ if (w.parked)
+ UNSAFE.unpark(w);
+ }
+ return true;
+ }
+
+ // Scanning for tasks
+
+ /**
+ * Scans for and, if found, executes one task. Scans start at a
+ * random index of workers array, and randomly select the first
+ * (2*#workers)-1 probes, and then, if all empty, resort to 2
+ * circular sweeps, which is necessary to check quiescence. and
+ * taking a submission only if no stealable tasks were found. The
+ * steal code inside the loop is a specialized form of
+ * ForkJoinWorkerThread.deqTask, followed bookkeeping to support
+ * helpJoinTask and signal propagation. The code for submission
+ * queues is almost identical. On each steal, the worker completes
+ * not only the task, but also all local tasks that this task may
+ * have generated. On detecting staleness or contention when
+ * trying to take a task, this method returns without finishing
+ * sweep, which allows global state rechecks before retry.
+ *
+ * @param w the worker
+ * @param a the number of active workers
+ * @return true if swept all queues without finding a task
+ */
+ private boolean scan(ForkJoinWorkerThread w, int a) {
+ int g = scanGuard; // mask 0 avoids useless scans if only one active
+ int m = (parallelism == 1 - a && blockedCount == 0) ? 0 : g & SMASK;
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws == null || ws.length <= m) // staleness check
+ return false;
+ for (int r = w.seed, k = r, j = -(m + m); j <= m + m; ++j) {
+ ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
+ ForkJoinWorkerThread v = ws[k & m];
+ if (v != null && (b = v.queueBase) != v.queueTop &&
+ (q = v.queue) != null && (i = (q.length - 1) & b) >= 0) {
+ long u = (i << ASHIFT) + ABASE;
+ if ((t = q[i]) != null && v.queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ int d = (v.queueBase = b + 1) - v.queueTop;
+ v.stealHint = w.poolIndex;
+ if (d != 0)
+ signalWork(); // propagate if nonempty
+ w.execTask(t);
+ }
+ r ^= r << 13; r ^= r >>> 17; w.seed = r ^ (r << 5);
+ return false; // store next seed
+ }
+ else if (j < 0) { // xorshift
+ r ^= r << 13; r ^= r >>> 17; k = r ^= r << 5;
+ }
+ else
+ ++k;
+ }
+ if (scanGuard != g) // staleness check
+ return false;
+ else { // try to take submission
+ ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
+ if ((b = queueBase) != queueTop &&
+ (q = submissionQueue) != null &&
+ (i = (q.length - 1) & b) >= 0) {
+ long u = (i << ASHIFT) + ABASE;
+ if ((t = q[i]) != null && queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ queueBase = b + 1;
+ w.execTask(t);
+ }
+ return false;
+ }
+ return true; // all queues empty
+ }
+ }
+
+ /**
+ * Tries to enqueue worker w in wait queue and await change in
+ * worker's eventCount. If the pool is quiescent and there is
+ * more than one worker, possibly terminates worker upon exit.
+ * Otherwise, before blocking, rescans queues to avoid missed
+ * signals. Upon finding work, releases at least one worker
+ * (which may be the current worker). Rescans restart upon
+ * detected staleness or failure to release due to
+ * contention. Note the unusual conventions about Thread.interrupt
+ * here and elsewhere: Because interrupts are used solely to alert
+ * threads to check termination, which is checked here anyway, we
+ * clear status (using Thread.interrupted) before any call to
+ * park, so that park does not immediately return due to status
+ * being set via some other unrelated call to interrupt in user
+ * code.
+ *
+ * @param w the calling worker
+ * @param c the ctl value on entry
+ * @return true if waited or another thread was released upon enq
+ */
+ private boolean tryAwaitWork(ForkJoinWorkerThread w, long c) {
+ int v = w.eventCount;
+ w.nextWait = (int)c; // w's successor record
+ long nc = (long)(v & E_MASK) | ((c - AC_UNIT) & (AC_MASK|TC_MASK));
+ if (ctl != c || !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
+ long d = ctl; // return true if lost to a deq, to force scan
+ return (int)d != (int)c && (d & AC_MASK) >= (c & AC_MASK);
+ }
+ for (int sc = w.stealCount; sc != 0;) { // accumulate stealCount
+ long s = stealCount;
+ if (UNSAFE.compareAndSwapLong(this, stealCountOffset, s, s + sc))
+ sc = w.stealCount = 0;
+ else if (w.eventCount != v)
+ return true; // update next time
+ }
+ if ((!shutdown || !tryTerminate(false)) &&
+ (int)c != 0 && parallelism + (int)(nc >> AC_SHIFT) == 0 &&
+ blockedCount == 0 && quiescerCount == 0)
+ idleAwaitWork(w, nc, c, v); // quiescent
+ for (boolean rescanned = false;;) {
+ if (w.eventCount != v)
+ return true;
+ if (!rescanned) {
+ int g = scanGuard, m = g & SMASK;
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws != null && m < ws.length) {
+ rescanned = true;
+ for (int i = 0; i <= m; ++i) {
+ ForkJoinWorkerThread u = ws[i];
+ if (u != null) {
+ if (u.queueBase != u.queueTop &&
+ !tryReleaseWaiter())
+ rescanned = false; // contended
+ if (w.eventCount != v)
+ return true;
+ }
+ }
+ }
+ if (scanGuard != g || // stale
+ (queueBase != queueTop && !tryReleaseWaiter()))
+ rescanned = false;
+ if (!rescanned)
+ Thread.yield(); // reduce contention
+ else
+ Thread.interrupted(); // clear before park
+ }
+ else {
+ w.parked = true; // must recheck
+ if (w.eventCount != v) {
+ w.parked = false;
+ return true;
+ }
+ LockSupport.park(this);
+ rescanned = w.parked = false;
+ }
+ }
+ }
+
+ /**
+ * If inactivating worker w has caused pool to become
+ * quiescent, check for pool termination, and wait for event
+ * for up to SHRINK_RATE nanosecs (rescans are unnecessary in
+ * this case because quiescence reflects consensus about lack
+ * of work). On timeout, if ctl has not changed, terminate the
+ * worker. Upon its termination (see deregisterWorker), it may
+ * wake up another worker to possibly repeat this process.
+ *
+ * @param w the calling worker
+ * @param currentCtl the ctl value after enqueuing w
+ * @param prevCtl the ctl value if w terminated
+ * @param v the eventCount w awaits change
+ */
+ private void idleAwaitWork(ForkJoinWorkerThread w, long currentCtl,
+ long prevCtl, int v) {
+ if (w.eventCount == v) {
+ if (shutdown)
+ tryTerminate(false);
+ ForkJoinTask.helpExpungeStaleExceptions(); // help clean weak refs
+ while (ctl == currentCtl) {
+ long startTime = System.nanoTime();
+ w.parked = true;
+ if (w.eventCount == v) // must recheck
+ LockSupport.parkNanos(this, SHRINK_RATE);
+ w.parked = false;
+ if (w.eventCount != v)
+ break;
+ else if (System.nanoTime() - startTime <
+ SHRINK_RATE - (SHRINK_RATE / 10)) // timing slop
+ Thread.interrupted(); // spurious wakeup
+ else if (UNSAFE.compareAndSwapLong(this, ctlOffset,
+ currentCtl, prevCtl)) {
+ w.terminate = true; // restore previous
+ w.eventCount = ((int)currentCtl + EC_UNIT) & E_MASK;
+ break;
+ }
+ }
+ }
+ }
+
+ // Submissions
+
+ /**
+ * Enqueues the given task in the submissionQueue. Same idea as
+ * ForkJoinWorkerThread.pushTask except for use of submissionLock.
+ *
+ * @param t the task
+ */
+ private void addSubmission(ForkJoinTask<?> t) {
+ final ReentrantLock lock = this.submissionLock;
+ lock.lock();
+ try {
+ ForkJoinTask<?>[] q; int s, m;
+ if ((q = submissionQueue) != null) { // ignore if queue removed
+ long u = (((s = queueTop) & (m = q.length-1)) << ASHIFT)+ABASE;
+ UNSAFE.putOrderedObject(q, u, t);
+ queueTop = s + 1;
+ if (s - queueBase == m)
+ growSubmissionQueue();
+ }
+ } finally {
+ lock.unlock();
+ }
+ signalWork();
+ }
+
+ // (pollSubmission is defined below with exported methods)
+
+ /**
+ * Creates or doubles submissionQueue array.
+ * Basically identical to ForkJoinWorkerThread version.
+ */
+ private void growSubmissionQueue() {
+ ForkJoinTask<?>[] oldQ = submissionQueue;
+ int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY;
+ if (size > MAXIMUM_QUEUE_CAPACITY)
+ throw new RejectedExecutionException("Queue capacity exceeded");
+ if (size < INITIAL_QUEUE_CAPACITY)
+ size = INITIAL_QUEUE_CAPACITY;
+ ForkJoinTask<?>[] q = submissionQueue = new ForkJoinTask<?>[size];
+ int mask = size - 1;
+ int top = queueTop;
+ int oldMask;
+ if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) {
+ for (int b = queueBase; b != top; ++b) {
+ long u = ((b & oldMask) << ASHIFT) + ABASE;
+ Object x = UNSAFE.getObjectVolatile(oldQ, u);
+ if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null))
+ UNSAFE.putObjectVolatile
+ (q, ((b & mask) << ASHIFT) + ABASE, x);
+ }
+ }
+ }
+
+ // Blocking support
+
+ /**
+ * Tries to increment blockedCount, decrement active count
+ * (sometimes implicitly) and possibly release or create a
+ * compensating worker in preparation for blocking. Fails
+ * on contention or termination.
+ *
+ * @return true if the caller can block, else should recheck and retry
+ */
+ private boolean tryPreBlock() {
+ int b = blockedCount;
+ if (UNSAFE.compareAndSwapInt(this, blockedCountOffset, b, b + 1)) {
+ int pc = parallelism;
+ do {
+ ForkJoinWorkerThread[] ws; ForkJoinWorkerThread w;
+ int e, ac, tc, i;
+ long c = ctl;
+ int u = (int)(c >>> 32);
+ if ((e = (int)c) < 0) {
+ // skip -- terminating
+ }
+ else if ((ac = (u >> UAC_SHIFT)) <= 0 && e != 0 &&
+ (ws = workers) != null &&
+ (i = ~e & SMASK) < ws.length &&
+ (w = ws[i]) != null) {
+ long nc = ((long)(w.nextWait & E_MASK) |
+ (c & (AC_MASK|TC_MASK)));
+ if (w.eventCount == e &&
+ UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
+ w.eventCount = (e + EC_UNIT) & E_MASK;
+ if (w.parked)
+ UNSAFE.unpark(w);
+ return true; // release an idle worker
+ }
+ }
+ else if ((tc = (short)(u >>> UTC_SHIFT)) >= 0 && ac + pc > 1) {
+ long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
+ if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc))
+ return true; // no compensation needed
+ }
+ else if (tc + pc < MAX_ID) {
+ long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
+ if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
+ addWorker();
+ return true; // create a replacement
+ }
+ }
+ // try to back out on any failure and let caller retry
+ } while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset,
+ b = blockedCount, b - 1));
+ }
+ return false;
+ }
+
+ /**
+ * Decrements blockedCount and increments active count.
+ */
+ private void postBlock() {
+ long c;
+ do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset, // no mask
+ c = ctl, c + AC_UNIT));
+ int b;
+ do {} while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset,
+ b = blockedCount, b - 1));
+ }
+
+ /**
+ * Possibly blocks waiting for the given task to complete, or
+ * cancels the task if terminating. Fails to wait if contended.
+ *
+ * @param joinMe the task
+ */
+ final void tryAwaitJoin(ForkJoinTask<?> joinMe) {
+ Thread.interrupted(); // clear interrupts before checking termination
+ if (joinMe.status >= 0) {
+ if (tryPreBlock()) {
+ joinMe.tryAwaitDone(0L);
+ postBlock();
+ }
+ else if ((ctl & STOP_BIT) != 0L)
+ joinMe.cancelIgnoringExceptions();
+ }
+ }
+
+ /**
+ * Possibly blocks the given worker waiting for joinMe to
+ * complete or timeout.
+ *
+ * @param joinMe the task
+ * @param nanos the wait time for underlying Object.wait
+ */
+ final void timedAwaitJoin(ForkJoinTask<?> joinMe, long nanos) {
+ while (joinMe.status >= 0) {
+ Thread.interrupted();
+ if ((ctl & STOP_BIT) != 0L) {
+ joinMe.cancelIgnoringExceptions();
+ break;
+ }
+ if (tryPreBlock()) {
+ long last = System.nanoTime();
+ while (joinMe.status >= 0) {
+ long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
+ if (millis <= 0)
+ break;
+ joinMe.tryAwaitDone(millis);
+ if (joinMe.status < 0)
+ break;
+ if ((ctl & STOP_BIT) != 0L) {
+ joinMe.cancelIgnoringExceptions();
+ break;
+ }
+ long now = System.nanoTime();
+ nanos -= now - last;
+ last = now;
+ }
+ postBlock();
+ break;
+ }
+ }
+ }
+
+ /**
+ * If necessary, compensates for blocker, and blocks.
+ */
+ private void awaitBlocker(ManagedBlocker blocker)
+ throws InterruptedException {
+ while (!blocker.isReleasable()) {
+ if (tryPreBlock()) {
+ try {
+ do {} while (!blocker.isReleasable() && !blocker.block());
+ } finally {
+ postBlock();
+ }
+ break;
+ }
+ }
+ }
+
+ // Creating, registering and deregistring workers
+
+ /**
+ * Tries to create and start a worker; minimally rolls back counts
+ * on failure.
+ */
+ private void addWorker() {
+ Throwable ex = null;
+ ForkJoinWorkerThread t = null;
+ try {
+ t = factory.newThread(this);
+ } catch (Throwable e) {
+ ex = e;
+ }
+ if (t == null) { // null or exceptional factory return
+ long c; // adjust counts
+ do {} while (!UNSAFE.compareAndSwapLong
+ (this, ctlOffset, c = ctl,
+ (((c - AC_UNIT) & AC_MASK) |
+ ((c - TC_UNIT) & TC_MASK) |
+ (c & ~(AC_MASK|TC_MASK)))));
+ // Propagate exception if originating from an external caller
+ if (!tryTerminate(false) && ex != null &&
+ !(Thread.currentThread() instanceof ForkJoinWorkerThread))
+ SneakyThrow.sneakyThrow(ex); // android-changed
+ }
+ else
+ t.start();
+ }
+
+ /**
+ * Callback from ForkJoinWorkerThread constructor to assign a
+ * public name
+ */
+ final String nextWorkerName() {
+ for (int n;;) {
+ if (UNSAFE.compareAndSwapInt(this, nextWorkerNumberOffset,
+ n = nextWorkerNumber, ++n))
+ return workerNamePrefix + n;
+ }
+ }
+
+ /**
+ * Callback from ForkJoinWorkerThread constructor to
+ * determine its poolIndex and record in workers array.
+ *
+ * @param w the worker
+ * @return the worker's pool index
+ */
+ final int registerWorker(ForkJoinWorkerThread w) {
+ /*
+ * In the typical case, a new worker acquires the lock, uses
+ * next available index and returns quickly. Since we should
+ * not block callers (ultimately from signalWork or
+ * tryPreBlock) waiting for the lock needed to do this, we
+ * instead help release other workers while waiting for the
+ * lock.
+ */
+ for (int g;;) {
+ ForkJoinWorkerThread[] ws;
+ if (((g = scanGuard) & SG_UNIT) == 0 &&
+ UNSAFE.compareAndSwapInt(this, scanGuardOffset,
+ g, g | SG_UNIT)) {
+ int k = nextWorkerIndex;
+ try {
+ if ((ws = workers) != null) { // ignore on shutdown
+ int n = ws.length;
+ if (k < 0 || k >= n || ws[k] != null) {
+ for (k = 0; k < n && ws[k] != null; ++k)
+ ;
+ if (k == n)
+ ws = workers = Arrays.copyOf(ws, n << 1);
+ }
+ ws[k] = w;
+ nextWorkerIndex = k + 1;
+ int m = g & SMASK;
+ g = (k > m) ? ((m << 1) + 1) & SMASK : g + (SG_UNIT<<1);
+ }
+ } finally {
+ scanGuard = g;
+ }
+ return k;
+ }
+ else if ((ws = workers) != null) { // help release others
+ for (ForkJoinWorkerThread u : ws) {
+ if (u != null && u.queueBase != u.queueTop) {
+ if (tryReleaseWaiter())
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Final callback from terminating worker. Removes record of
+ * worker from array, and adjusts counts. If pool is shutting
+ * down, tries to complete termination.
+ *
+ * @param w the worker
+ */
+ final void deregisterWorker(ForkJoinWorkerThread w, Throwable ex) {
+ int idx = w.poolIndex;
+ int sc = w.stealCount;
+ int steps = 0;
+ // Remove from array, adjust worker counts and collect steal count.
+ // We can intermix failed removes or adjusts with steal updates
+ do {
+ long s, c;
+ int g;
+ if (steps == 0 && ((g = scanGuard) & SG_UNIT) == 0 &&
+ UNSAFE.compareAndSwapInt(this, scanGuardOffset,
+ g, g |= SG_UNIT)) {
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws != null && idx >= 0 &&
+ idx < ws.length && ws[idx] == w)
+ ws[idx] = null; // verify
+ nextWorkerIndex = idx;
+ scanGuard = g + SG_UNIT;
+ steps = 1;
+ }
+ if (steps == 1 &&
+ UNSAFE.compareAndSwapLong(this, ctlOffset, c = ctl,
+ (((c - AC_UNIT) & AC_MASK) |
+ ((c - TC_UNIT) & TC_MASK) |
+ (c & ~(AC_MASK|TC_MASK)))))
+ steps = 2;
+ if (sc != 0 &&
+ UNSAFE.compareAndSwapLong(this, stealCountOffset,
+ s = stealCount, s + sc))
+ sc = 0;
+ } while (steps != 2 || sc != 0);
+ if (!tryTerminate(false)) {
+ if (ex != null) // possibly replace if died abnormally
+ signalWork();
+ else
+ tryReleaseWaiter();
+ }
+ }
+
+ // Shutdown and termination
+
+ /**
+ * Possibly initiates and/or completes termination.
+ *
+ * @param now if true, unconditionally terminate, else only
+ * if shutdown and empty queue and no active workers
+ * @return true if now terminating or terminated
+ */
+ private boolean tryTerminate(boolean now) {
+ long c;
+ while (((c = ctl) & STOP_BIT) == 0) {
+ if (!now) {
+ if ((int)(c >> AC_SHIFT) != -parallelism)
+ return false;
+ if (!shutdown || blockedCount != 0 || quiescerCount != 0 ||
+ queueBase != queueTop) {
+ if (ctl == c) // staleness check
+ return false;
+ continue;
+ }
+ }
+ if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, c | STOP_BIT))
+ startTerminating();
+ }
+ if ((short)(c >>> TC_SHIFT) == -parallelism) { // signal when 0 workers
+ final ReentrantLock lock = this.submissionLock;
+ lock.lock();
+ try {
+ termination.signalAll();
+ } finally {
+ lock.unlock();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Runs up to three passes through workers: (0) Setting
+ * termination status for each worker, followed by wakeups up to
+ * queued workers; (1) helping cancel tasks; (2) interrupting
+ * lagging threads (likely in external tasks, but possibly also
+ * blocked in joins). Each pass repeats previous steps because of
+ * potential lagging thread creation.
+ */
+ private void startTerminating() {
+ cancelSubmissions();
+ for (int pass = 0; pass < 3; ++pass) {
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws != null) {
+ for (ForkJoinWorkerThread w : ws) {
+ if (w != null) {
+ w.terminate = true;
+ if (pass > 0) {
+ w.cancelTasks();
+ if (pass > 1 && !w.isInterrupted()) {
+ try {
+ w.interrupt();
+ } catch (SecurityException ignore) {
+ }
+ }
+ }
+ }
+ }
+ terminateWaiters();
+ }
+ }
+ }
+
+ /**
+ * Polls and cancels all submissions. Called only during termination.
+ */
+ private void cancelSubmissions() {
+ while (queueBase != queueTop) {
+ ForkJoinTask<?> task = pollSubmission();
+ if (task != null) {
+ try {
+ task.cancel(false);
+ } catch (Throwable ignore) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Tries to set the termination status of waiting workers, and
+ * then wakes them up (after which they will terminate).
+ */
+ private void terminateWaiters() {
+ ForkJoinWorkerThread[] ws = workers;
+ if (ws != null) {
+ ForkJoinWorkerThread w; long c; int i, e;
+ int n = ws.length;
+ while ((i = ~(e = (int)(c = ctl)) & SMASK) < n &&
+ (w = ws[i]) != null && w.eventCount == (e & E_MASK)) {
+ if (UNSAFE.compareAndSwapLong(this, ctlOffset, c,
+ (long)(w.nextWait & E_MASK) |
+ ((c + AC_UNIT) & AC_MASK) |
+ (c & (TC_MASK|STOP_BIT)))) {
+ w.terminate = true;
+ w.eventCount = e + EC_UNIT;
+ if (w.parked)
+ UNSAFE.unpark(w);
+ }
+ }
+ }
+ }
+
+ // misc ForkJoinWorkerThread support
+
+ /**
+ * Increments or decrements quiescerCount. Needed only to prevent
+ * triggering shutdown if a worker is transiently inactive while
+ * checking quiescence.
+ *
+ * @param delta 1 for increment, -1 for decrement
+ */
+ final void addQuiescerCount(int delta) {
+ int c;
+ do {} while (!UNSAFE.compareAndSwapInt(this, quiescerCountOffset,
+ c = quiescerCount, c + delta));
+ }
+
+ /**
+ * Directly increments or decrements active count without queuing.
+ * This method is used to transiently assert inactivation while
+ * checking quiescence.
+ *
+ * @param delta 1 for increment, -1 for decrement
+ */
+ final void addActiveCount(int delta) {
+ long d = (long)delta << AC_SHIFT;
+ long c;
+ do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset,
+ c = ctl, c + d));
+ }
+
+ /**
+ * Returns the approximate (non-atomic) number of idle threads per
+ * active thread.
+ */
+ final int idlePerActive() {
+ // Approximate at powers of two for small values, saturate past 4
+ int p = parallelism;
+ int a = p + (int)(ctl >> AC_SHIFT);
+ return (a > (p >>>= 1) ? 0 :
+ a > (p >>>= 1) ? 1 :
+ a > (p >>>= 1) ? 2 :
+ a > (p >>>= 1) ? 4 :
+ 8);
+ }
+
+ // Exported methods
+
+ // Constructors
+
+ /**
+ * Creates a {@code ForkJoinPool} with parallelism equal to {@link
+ * java.lang.Runtime#availableProcessors}, using the {@linkplain
+ * #defaultForkJoinWorkerThreadFactory default thread factory},
+ * no UncaughtExceptionHandler, and non-async LIFO processing mode.
+ */
+ public ForkJoinPool() {
+ this(Runtime.getRuntime().availableProcessors(),
+ defaultForkJoinWorkerThreadFactory, null, false);
+ }
+
+ /**
+ * Creates a {@code ForkJoinPool} with the indicated parallelism
+ * level, the {@linkplain
+ * #defaultForkJoinWorkerThreadFactory default thread factory},
+ * no UncaughtExceptionHandler, and non-async LIFO processing mode.
+ *
+ * @param parallelism the parallelism level
+ * @throws IllegalArgumentException if parallelism less than or
+ * equal to zero, or greater than implementation limit
+ */
+ public ForkJoinPool(int parallelism) {
+ this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
+ }
+
+ /**
+ * Creates a {@code ForkJoinPool} with the given parameters.
+ *
+ * @param parallelism the parallelism level. For default value,
+ * use {@link java.lang.Runtime#availableProcessors}.
+ * @param factory the factory for creating new threads. For default value,
+ * use {@link #defaultForkJoinWorkerThreadFactory}.
+ * @param handler the handler for internal worker threads that
+ * terminate due to unrecoverable errors encountered while executing
+ * tasks. For default value, use {@code null}.
+ * @param asyncMode if true,
+ * establishes local first-in-first-out scheduling mode for forked
+ * tasks that are never joined. This mode may be more appropriate
+ * than default locally stack-based mode in applications in which
+ * worker threads only process event-style asynchronous tasks.
+ * For default value, use {@code false}.
+ * @throws IllegalArgumentException if parallelism less than or
+ * equal to zero, or greater than implementation limit
+ * @throws NullPointerException if the factory is null
+ */
+ public ForkJoinPool(int parallelism,
+ ForkJoinWorkerThreadFactory factory,
+ Thread.UncaughtExceptionHandler handler,
+ boolean asyncMode) {
+ checkPermission();
+ if (factory == null)
+ throw new NullPointerException();
+ if (parallelism <= 0 || parallelism > MAX_ID)
+ throw new IllegalArgumentException();
+ this.parallelism = parallelism;
+ this.factory = factory;
+ this.ueh = handler;
+ this.locallyFifo = asyncMode;
+ long np = (long)(-parallelism); // offset ctl counts
+ this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
+ this.submissionQueue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
+ // initialize workers array with room for 2*parallelism if possible
+ int n = parallelism << 1;
+ if (n >= MAX_ID)
+ n = MAX_ID;
+ else { // See Hackers Delight, sec 3.2, where n < (1 << 16)
+ n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8;
+ }
+ workers = new ForkJoinWorkerThread[n + 1];
+ this.submissionLock = new ReentrantLock();
+ this.termination = submissionLock.newCondition();
+ StringBuilder sb = new StringBuilder("ForkJoinPool-");
+ sb.append(poolNumberGenerator.incrementAndGet());
+ sb.append("-worker-");
+ this.workerNamePrefix = sb.toString();
+ }
+
+ // Execution methods
+
+ /**
+ * Performs the given task, returning its result upon completion.
+ * If the computation encounters an unchecked Exception or Error,
+ * it is rethrown as the outcome of this invocation. Rethrown
+ * exceptions behave in the same way as regular exceptions, but,
+ * when possible, contain stack traces (as displayed for example
+ * using {@code ex.printStackTrace()}) of both the current thread
+ * as well as the thread actually encountering the exception;
+ * minimally only the latter.
+ *
+ * @param task the task
+ * @return the task's result
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public <T> T invoke(ForkJoinTask<T> task) {
+ Thread t = Thread.currentThread();
+ if (task == null)
+ throw new NullPointerException();
+ if (shutdown)
+ throw new RejectedExecutionException();
+ if ((t instanceof ForkJoinWorkerThread) &&
+ ((ForkJoinWorkerThread)t).pool == this)
+ return task.invoke(); // bypass submit if in same pool
+ else {
+ addSubmission(task);
+ return task.join();
+ }
+ }
+
+ /**
+ * Unless terminating, forks task if within an ongoing FJ
+ * computation in the current pool, else submits as external task.
+ */
+ private <T> void forkOrSubmit(ForkJoinTask<T> task) {
+ ForkJoinWorkerThread w;
+ Thread t = Thread.currentThread();
+ if (shutdown)
+ throw new RejectedExecutionException();
+ if ((t instanceof ForkJoinWorkerThread) &&
+ (w = (ForkJoinWorkerThread)t).pool == this)
+ w.pushTask(task);
+ else
+ addSubmission(task);
+ }
+
+ /**
+ * Arranges for (asynchronous) execution of the given task.
+ *
+ * @param task the task
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public void execute(ForkJoinTask<?> task) {
+ if (task == null)
+ throw new NullPointerException();
+ forkOrSubmit(task);
+ }
+
+ // AbstractExecutorService methods
+
+ /**
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public void execute(Runnable task) {
+ if (task == null)
+ throw new NullPointerException();
+ ForkJoinTask<?> job;
+ if (task instanceof ForkJoinTask<?>) // avoid re-wrap
+ job = (ForkJoinTask<?>) task;
+ else
+ job = ForkJoinTask.adapt(task, null);
+ forkOrSubmit(job);
+ }
+
+ /**
+ * Submits a ForkJoinTask for execution.
+ *
+ * @param task the task to submit
+ * @return the task
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
+ if (task == null)
+ throw new NullPointerException();
+ forkOrSubmit(task);
+ return task;
+ }
+
+ /**
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public <T> ForkJoinTask<T> submit(Callable<T> task) {
+ if (task == null)
+ throw new NullPointerException();
+ ForkJoinTask<T> job = ForkJoinTask.adapt(task);
+ forkOrSubmit(job);
+ return job;
+ }
+
+ /**
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public <T> ForkJoinTask<T> submit(Runnable task, T result) {
+ if (task == null)
+ throw new NullPointerException();
+ ForkJoinTask<T> job = ForkJoinTask.adapt(task, result);
+ forkOrSubmit(job);
+ return job;
+ }
+
+ /**
+ * @throws NullPointerException if the task is null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ */
+ public ForkJoinTask<?> submit(Runnable task) {
+ if (task == null)
+ throw new NullPointerException();
+ ForkJoinTask<?> job;
+ if (task instanceof ForkJoinTask<?>) // avoid re-wrap
+ job = (ForkJoinTask<?>) task;
+ else
+ job = ForkJoinTask.adapt(task, null);
+ forkOrSubmit(job);
+ return job;
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ * @throws RejectedExecutionException {@inheritDoc}
+ */
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
+ ArrayList<ForkJoinTask<T>> forkJoinTasks =
+ new ArrayList<ForkJoinTask<T>>(tasks.size());
+ for (Callable<T> task : tasks)
+ forkJoinTasks.add(ForkJoinTask.adapt(task));
+ invoke(new InvokeAll<T>(forkJoinTasks));
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ List<Future<T>> futures = (List<Future<T>>) (List) forkJoinTasks;
+ return futures;
+ }
+
+ static final class InvokeAll<T> extends RecursiveAction {
+ final ArrayList<ForkJoinTask<T>> tasks;
+ InvokeAll(ArrayList<ForkJoinTask<T>> tasks) { this.tasks = tasks; }
+ public void compute() {
+ try { invokeAll(tasks); }
+ catch (Exception ignore) {}
+ }
+ private static final long serialVersionUID = -7914297376763021607L;
+ }
+
+ /**
+ * Returns the factory used for constructing new workers.
+ *
+ * @return the factory used for constructing new workers
+ */
+ public ForkJoinWorkerThreadFactory getFactory() {
+ return factory;
+ }
+
+ /**
+ * Returns the handler for internal worker threads that terminate
+ * due to unrecoverable errors encountered while executing tasks.
+ *
+ * @return the handler, or {@code null} if none
+ */
+ public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
+ return ueh;
+ }
+
+ /**
+ * Returns the targeted parallelism level of this pool.
+ *
+ * @return the targeted parallelism level of this pool
+ */
+ public int getParallelism() {
+ return parallelism;
+ }
+
+ /**
+ * Returns the number of worker threads that have started but not
+ * yet terminated. The result returned by this method may differ
+ * from {@link #getParallelism} when threads are created to
+ * maintain parallelism when others are cooperatively blocked.
+ *
+ * @return the number of worker threads
+ */
+ public int getPoolSize() {
+ return parallelism + (short)(ctl >>> TC_SHIFT);
+ }
+
+ /**
+ * Returns {@code true} if this pool uses local first-in-first-out
+ * scheduling mode for forked tasks that are never joined.
+ *
+ * @return {@code true} if this pool uses async mode
+ */
+ public boolean getAsyncMode() {
+ return locallyFifo;
+ }
+
+ /**
+ * Returns an estimate of the number of worker threads that are
+ * not blocked waiting to join tasks or for other managed
+ * synchronization. This method may overestimate the
+ * number of running threads.
+ *
+ * @return the number of worker threads
+ */
+ public int getRunningThreadCount() {
+ int r = parallelism + (int)(ctl >> AC_SHIFT);
+ return (r <= 0) ? 0 : r; // suppress momentarily negative values
+ }
+
+ /**
+ * Returns an estimate of the number of threads that are currently
+ * stealing or executing tasks. This method may overestimate the
+ * number of active threads.
+ *
+ * @return the number of active threads
+ */
+ public int getActiveThreadCount() {
+ int r = parallelism + (int)(ctl >> AC_SHIFT) + blockedCount;
+ return (r <= 0) ? 0 : r; // suppress momentarily negative values
+ }
+
+ /**
+ * Returns {@code true} if all worker threads are currently idle.
+ * An idle worker is one that cannot obtain a task to execute
+ * because none are available to steal from other threads, and
+ * there are no pending submissions to the pool. This method is
+ * conservative; it might not return {@code true} immediately upon
+ * idleness of all threads, but will eventually become true if
+ * threads remain inactive.
+ *
+ * @return {@code true} if all threads are currently idle
+ */
+ public boolean isQuiescent() {
+ return parallelism + (int)(ctl >> AC_SHIFT) + blockedCount == 0;
+ }
+
+ /**
+ * Returns an estimate of the total number of tasks stolen from
+ * one thread's work queue by another. The reported value
+ * underestimates the actual total number of steals when the pool
+ * is not quiescent. This value may be useful for monitoring and
+ * tuning fork/join programs: in general, steal counts should be
+ * high enough to keep threads busy, but low enough to avoid
+ * overhead and contention across threads.
+ *
+ * @return the number of steals
+ */
+ public long getStealCount() {
+ return stealCount;
+ }
+
+ /**
+ * Returns an estimate of the total number of tasks currently held
+ * in queues by worker threads (but not including tasks submitted
+ * to the pool that have not begun executing). This value is only
+ * an approximation, obtained by iterating across all threads in
+ * the pool. This method may be useful for tuning task
+ * granularities.
+ *
+ * @return the number of queued tasks
+ */
+ public long getQueuedTaskCount() {
+ long count = 0;
+ ForkJoinWorkerThread[] ws;
+ if ((short)(ctl >>> TC_SHIFT) > -parallelism &&
+ (ws = workers) != null) {
+ for (ForkJoinWorkerThread w : ws)
+ if (w != null)
+ count -= w.queueBase - w.queueTop; // must read base first
+ }
+ return count;
+ }
+
+ /**
+ * Returns an estimate of the number of tasks submitted to this
+ * pool that have not yet begun executing. This method may take
+ * time proportional to the number of submissions.
+ *
+ * @return the number of queued submissions
+ */
+ public int getQueuedSubmissionCount() {
+ return -queueBase + queueTop;
+ }
+
+ /**
+ * Returns {@code true} if there are any tasks submitted to this
+ * pool that have not yet begun executing.
+ *
+ * @return {@code true} if there are any queued submissions
+ */
+ public boolean hasQueuedSubmissions() {
+ return queueBase != queueTop;
+ }
+
+ /**
+ * Removes and returns the next unexecuted submission if one is
+ * available. This method may be useful in extensions to this
+ * class that re-assign work in systems with multiple pools.
+ *
+ * @return the next submission, or {@code null} if none
+ */
+ protected ForkJoinTask<?> pollSubmission() {
+ ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
+ while ((b = queueBase) != queueTop &&
+ (q = submissionQueue) != null &&
+ (i = (q.length - 1) & b) >= 0) {
+ long u = (i << ASHIFT) + ABASE;
+ if ((t = q[i]) != null &&
+ queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ queueBase = b + 1;
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes all available unexecuted submitted and forked tasks
+ * from scheduling queues and adds them to the given collection,
+ * without altering their execution status. These may include
+ * artificially generated or wrapped tasks. This method is
+ * designed to be invoked only when the pool is known to be
+ * quiescent. Invocations at other times may not remove all
+ * tasks. A failure encountered while attempting to add elements
+ * to collection {@code c} may result in elements being in
+ * neither, either or both collections when the associated
+ * exception is thrown. The behavior of this operation is
+ * undefined if the specified collection is modified while the
+ * operation is in progress.
+ *
+ * @param c the collection to transfer elements into
+ * @return the number of elements transferred
+ */
+ protected int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
+ int count = 0;
+ while (queueBase != queueTop) {
+ ForkJoinTask<?> t = pollSubmission();
+ if (t != null) {
+ c.add(t);
+ ++count;
+ }
+ }
+ ForkJoinWorkerThread[] ws;
+ if ((short)(ctl >>> TC_SHIFT) > -parallelism &&
+ (ws = workers) != null) {
+ for (ForkJoinWorkerThread w : ws)
+ if (w != null)
+ count += w.drainTasksTo(c);
+ }
+ return count;
+ }
+
+ /**
+ * Returns a string identifying this pool, as well as its state,
+ * including indications of run state, parallelism level, and
+ * worker and task counts.
+ *
+ * @return a string identifying this pool, as well as its state
+ */
+ public String toString() {
+ long st = getStealCount();
+ long qt = getQueuedTaskCount();
+ long qs = getQueuedSubmissionCount();
+ int pc = parallelism;
+ long c = ctl;
+ int tc = pc + (short)(c >>> TC_SHIFT);
+ int rc = pc + (int)(c >> AC_SHIFT);
+ if (rc < 0) // ignore transient negative
+ rc = 0;
+ int ac = rc + blockedCount;
+ String level;
+ if ((c & STOP_BIT) != 0)
+ level = (tc == 0) ? "Terminated" : "Terminating";
+ else
+ level = shutdown ? "Shutting down" : "Running";
+ return super.toString() +
+ "[" + level +
+ ", parallelism = " + pc +
+ ", size = " + tc +
+ ", active = " + ac +
+ ", running = " + rc +
+ ", steals = " + st +
+ ", tasks = " + qt +
+ ", submissions = " + qs +
+ "]";
+ }
+
+ /**
+ * Initiates an orderly shutdown in which previously submitted
+ * tasks are executed, but no new tasks will be accepted.
+ * Invocation has no additional effect if already shut down.
+ * Tasks that are in the process of being submitted concurrently
+ * during the course of this method may or may not be rejected.
+ */
+ public void shutdown() {
+ checkPermission();
+ shutdown = true;
+ tryTerminate(false);
+ }
+
+ /**
+ * Attempts to cancel and/or stop all tasks, and reject all
+ * subsequently submitted tasks. Tasks that are in the process of
+ * being submitted or executed concurrently during the course of
+ * this method may or may not be rejected. This method cancels
+ * both existing and unexecuted tasks, in order to permit
+ * termination in the presence of task dependencies. So the method
+ * always returns an empty list (unlike the case for some other
+ * Executors).
+ *
+ * @return an empty list
+ */
+ public List<Runnable> shutdownNow() {
+ checkPermission();
+ shutdown = true;
+ tryTerminate(true);
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns {@code true} if all tasks have completed following shut down.
+ *
+ * @return {@code true} if all tasks have completed following shut down
+ */
+ public boolean isTerminated() {
+ long c = ctl;
+ return ((c & STOP_BIT) != 0L &&
+ (short)(c >>> TC_SHIFT) == -parallelism);
+ }
+
+ /**
+ * Returns {@code true} if the process of termination has
+ * commenced but not yet completed. This method may be useful for
+ * debugging. A return of {@code true} reported a sufficient
+ * period after shutdown may indicate that submitted tasks have
+ * ignored or suppressed interruption, or are waiting for IO,
+ * causing this executor not to properly terminate. (See the
+ * advisory notes for class {@link ForkJoinTask} stating that
+ * tasks should not normally entail blocking operations. But if
+ * they do, they must abort them on interrupt.)
+ *
+ * @return {@code true} if terminating but not yet terminated
+ */
+ public boolean isTerminating() {
+ long c = ctl;
+ return ((c & STOP_BIT) != 0L &&
+ (short)(c >>> TC_SHIFT) != -parallelism);
+ }
+
+ /**
+ * Returns true if terminating or terminated. Used by ForkJoinWorkerThread.
+ */
+ final boolean isAtLeastTerminating() {
+ return (ctl & STOP_BIT) != 0L;
+ }
+
+ /**
+ * Returns {@code true} if this pool has been shut down.
+ *
+ * @return {@code true} if this pool has been shut down
+ */
+ public boolean isShutdown() {
+ return shutdown;
+ }
+
+ /**
+ * Blocks until all tasks have completed execution after a shutdown
+ * request, or the timeout occurs, or the current thread is
+ * interrupted, whichever happens first.
+ *
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the timeout argument
+ * @return {@code true} if this executor terminated and
+ * {@code false} if the timeout elapsed before termination
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public boolean awaitTermination(long timeout, TimeUnit unit)
+ throws InterruptedException {
+ long nanos = unit.toNanos(timeout);
+ final ReentrantLock lock = this.submissionLock;
+ lock.lock();
+ try {
+ for (;;) {
+ if (isTerminated())
+ return true;
+ if (nanos <= 0)
+ return false;
+ nanos = termination.awaitNanos(nanos);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Interface for extending managed parallelism for tasks running
+ * in {@link ForkJoinPool}s.
+ *
+ * <p>A {@code ManagedBlocker} provides two methods. Method
+ * {@code isReleasable} must return {@code true} if blocking is
+ * not necessary. Method {@code block} blocks the current thread
+ * if necessary (perhaps internally invoking {@code isReleasable}
+ * before actually blocking). These actions are performed by any
+ * thread invoking {@link ForkJoinPool#managedBlock}. The
+ * unusual methods in this API accommodate synchronizers that may,
+ * but don't usually, block for long periods. Similarly, they
+ * allow more efficient internal handling of cases in which
+ * additional workers may be, but usually are not, needed to
+ * ensure sufficient parallelism. Toward this end,
+ * implementations of method {@code isReleasable} must be amenable
+ * to repeated invocation.
+ *
+ * <p>For example, here is a ManagedBlocker based on a
+ * ReentrantLock:
+ * <pre> {@code
+ * class ManagedLocker implements ManagedBlocker {
+ * final ReentrantLock lock;
+ * boolean hasLock = false;
+ * ManagedLocker(ReentrantLock lock) { this.lock = lock; }
+ * public boolean block() {
+ * if (!hasLock)
+ * lock.lock();
+ * return true;
+ * }
+ * public boolean isReleasable() {
+ * return hasLock || (hasLock = lock.tryLock());
+ * }
+ * }}</pre>
+ *
+ * <p>Here is a class that possibly blocks waiting for an
+ * item on a given queue:
+ * <pre> {@code
+ * class QueueTaker<E> implements ManagedBlocker {
+ * final BlockingQueue<E> queue;
+ * volatile E item = null;
+ * QueueTaker(BlockingQueue<E> q) { this.queue = q; }
+ * public boolean block() throws InterruptedException {
+ * if (item == null)
+ * item = queue.take();
+ * return true;
+ * }
+ * public boolean isReleasable() {
+ * return item != null || (item = queue.poll()) != null;
+ * }
+ * public E getItem() { // call after pool.managedBlock completes
+ * return item;
+ * }
+ * }}</pre>
+ */
+ public static interface ManagedBlocker {
+ /**
+ * Possibly blocks the current thread, for example waiting for
+ * a lock or condition.
+ *
+ * @return {@code true} if no additional blocking is necessary
+ * (i.e., if isReleasable would return true)
+ * @throws InterruptedException if interrupted while waiting
+ * (the method is not required to do so, but is allowed to)
+ */
+ boolean block() throws InterruptedException;
+
+ /**
+ * Returns {@code true} if blocking is unnecessary.
+ */
+ boolean isReleasable();
+ }
+
+ /**
+ * Blocks in accord with the given blocker. If the current thread
+ * is a {@link ForkJoinWorkerThread}, this method possibly
+ * arranges for a spare thread to be activated if necessary to
+ * ensure sufficient parallelism while the current thread is blocked.
+ *
+ * <p>If the caller is not a {@link ForkJoinTask}, this method is
+ * behaviorally equivalent to
+ * <pre> {@code
+ * while (!blocker.isReleasable())
+ * if (blocker.block())
+ * return;
+ * }</pre>
+ *
+ * If the caller is a {@code ForkJoinTask}, then the pool may
+ * first be expanded to ensure parallelism, and later adjusted.
+ *
+ * @param blocker the blocker
+ * @throws InterruptedException if blocker.block did so
+ */
+ public static void managedBlock(ManagedBlocker blocker)
+ throws InterruptedException {
+ Thread t = Thread.currentThread();
+ if (t instanceof ForkJoinWorkerThread) {
+ ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
+ w.pool.awaitBlocker(blocker);
+ }
+ else {
+ do {} while (!blocker.isReleasable() && !blocker.block());
+ }
+ }
+
+ // AbstractExecutorService overrides. These rely on undocumented
+ // fact that ForkJoinTask.adapt returns ForkJoinTasks that also
+ // implement RunnableFuture.
+
+ protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
+ return (RunnableFuture<T>) ForkJoinTask.adapt(runnable, value);
+ }
+
+ protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
+ return (RunnableFuture<T>) ForkJoinTask.adapt(callable);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long ctlOffset;
+ private static final long stealCountOffset;
+ private static final long blockedCountOffset;
+ private static final long quiescerCountOffset;
+ private static final long scanGuardOffset;
+ private static final long nextWorkerNumberOffset;
+ private static final long ABASE;
+ private static final int ASHIFT;
+
+ static {
+ poolNumberGenerator = new AtomicInteger();
+ workerSeedGenerator = new Random();
+ modifyThreadPermission = new RuntimePermission("modifyThread");
+ defaultForkJoinWorkerThreadFactory =
+ new DefaultForkJoinWorkerThreadFactory();
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ForkJoinPool.class;
+ ctlOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("ctl"));
+ stealCountOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("stealCount"));
+ blockedCountOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("blockedCount"));
+ quiescerCountOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("quiescerCount"));
+ scanGuardOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("scanGuard"));
+ nextWorkerNumberOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("nextWorkerNumber"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ Class<?> a = ForkJoinTask[].class;
+ ABASE = UNSAFE.arrayBaseOffset(a);
+ int s = UNSAFE.arrayIndexScale(a);
+ if ((s & (s-1)) != 0)
+ throw new Error("data type scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
new file mode 100644
index 0000000..86a29d7
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
@@ -0,0 +1,1357 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.ReentrantLock;
+import java.lang.reflect.Constructor;
+import libcore.util.SneakyThrow;
+
+/**
+ * Abstract base class for tasks that run within a {@link ForkJoinPool}.
+ * A {@code ForkJoinTask} is a thread-like entity that is much
+ * lighter weight than a normal thread. Huge numbers of tasks and
+ * subtasks may be hosted by a small number of actual threads in a
+ * ForkJoinPool, at the price of some usage limitations.
+ *
+ * <p>A "main" {@code ForkJoinTask} begins execution when submitted
+ * to a {@link ForkJoinPool}. Once started, it will usually in turn
+ * start other subtasks. As indicated by the name of this class,
+ * many programs using {@code ForkJoinTask} employ only methods
+ * {@link #fork} and {@link #join}, or derivatives such as {@link
+ * #invokeAll(ForkJoinTask...) invokeAll}. However, this class also
+ * provides a number of other methods that can come into play in
+ * advanced usages, as well as extension mechanics that allow
+ * support of new forms of fork/join processing.
+ *
+ * <p>A {@code ForkJoinTask} is a lightweight form of {@link Future}.
+ * The efficiency of {@code ForkJoinTask}s stems from a set of
+ * restrictions (that are only partially statically enforceable)
+ * reflecting their intended use as computational tasks calculating
+ * pure functions or operating on purely isolated objects. The
+ * primary coordination mechanisms are {@link #fork}, that arranges
+ * asynchronous execution, and {@link #join}, that doesn't proceed
+ * until the task's result has been computed. Computations should
+ * avoid {@code synchronized} methods or blocks, and should minimize
+ * other blocking synchronization apart from joining other tasks or
+ * using synchronizers such as Phasers that are advertised to
+ * cooperate with fork/join scheduling. Tasks should also not perform
+ * blocking IO, and should ideally access variables that are
+ * completely independent of those accessed by other running
+ * tasks. Minor breaches of these restrictions, for example using
+ * shared output streams, may be tolerable in practice, but frequent
+ * use may result in poor performance, and the potential to
+ * indefinitely stall if the number of threads not waiting for IO or
+ * other external synchronization becomes exhausted. This usage
+ * restriction is in part enforced by not permitting checked
+ * exceptions such as {@code IOExceptions} to be thrown. However,
+ * computations may still encounter unchecked exceptions, that are
+ * rethrown to callers attempting to join them. These exceptions may
+ * additionally include {@link RejectedExecutionException} stemming
+ * from internal resource exhaustion, such as failure to allocate
+ * internal task queues. Rethrown exceptions behave in the same way as
+ * regular exceptions, but, when possible, contain stack traces (as
+ * displayed for example using {@code ex.printStackTrace()}) of both
+ * the thread that initiated the computation as well as the thread
+ * actually encountering the exception; minimally only the latter.
+ *
+ * <p>The primary method for awaiting completion and extracting
+ * results of a task is {@link #join}, but there are several variants:
+ * The {@link Future#get} methods support interruptible and/or timed
+ * waits for completion and report results using {@code Future}
+ * conventions. Method {@link #invoke} is semantically
+ * equivalent to {@code fork(); join()} but always attempts to begin
+ * execution in the current thread. The "<em>quiet</em>" forms of
+ * these methods do not extract results or report exceptions. These
+ * may be useful when a set of tasks are being executed, and you need
+ * to delay processing of results or exceptions until all complete.
+ * Method {@code invokeAll} (available in multiple versions)
+ * performs the most common form of parallel invocation: forking a set
+ * of tasks and joining them all.
+ *
+ * <p>The execution status of tasks may be queried at several levels
+ * of detail: {@link #isDone} is true if a task completed in any way
+ * (including the case where a task was cancelled without executing);
+ * {@link #isCompletedNormally} is true if a task completed without
+ * cancellation or encountering an exception; {@link #isCancelled} is
+ * true if the task was cancelled (in which case {@link #getException}
+ * returns a {@link java.util.concurrent.CancellationException}); and
+ * {@link #isCompletedAbnormally} is true if a task was either
+ * cancelled or encountered an exception, in which case {@link
+ * #getException} will return either the encountered exception or
+ * {@link java.util.concurrent.CancellationException}.
+ *
+ * <p>The ForkJoinTask class is not usually directly subclassed.
+ * Instead, you subclass one of the abstract classes that support a
+ * particular style of fork/join processing, typically {@link
+ * RecursiveAction} for computations that do not return results, or
+ * {@link RecursiveTask} for those that do. Normally, a concrete
+ * ForkJoinTask subclass declares fields comprising its parameters,
+ * established in a constructor, and then defines a {@code compute}
+ * method that somehow uses the control methods supplied by this base
+ * class. While these methods have {@code public} access (to allow
+ * instances of different task subclasses to call each other's
+ * methods), some of them may only be called from within other
+ * ForkJoinTasks (as may be determined using method {@link
+ * #inForkJoinPool}). Attempts to invoke them in other contexts
+ * result in exceptions or errors, possibly including
+ * {@code ClassCastException}.
+ *
+ * <p>Method {@link #join} and its variants are appropriate for use
+ * only when completion dependencies are acyclic; that is, the
+ * parallel computation can be described as a directed acyclic graph
+ * (DAG). Otherwise, executions may encounter a form of deadlock as
+ * tasks cyclically wait for each other. However, this framework
+ * supports other methods and techniques (for example the use of
+ * {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that
+ * may be of use in constructing custom subclasses for problems that
+ * are not statically structured as DAGs.
+ *
+ * <p>Most base support methods are {@code final}, to prevent
+ * overriding of implementations that are intrinsically tied to the
+ * underlying lightweight task scheduling framework. Developers
+ * creating new basic styles of fork/join processing should minimally
+ * implement {@code protected} methods {@link #exec}, {@link
+ * #setRawResult}, and {@link #getRawResult}, while also introducing
+ * an abstract computational method that can be implemented in its
+ * subclasses, possibly relying on other {@code protected} methods
+ * provided by this class.
+ *
+ * <p>ForkJoinTasks should perform relatively small amounts of
+ * computation. Large tasks should be split into smaller subtasks,
+ * usually via recursive decomposition. As a very rough rule of thumb,
+ * a task should perform more than 100 and less than 10000 basic
+ * computational steps, and should avoid indefinite looping. If tasks
+ * are too big, then parallelism cannot improve throughput. If too
+ * small, then memory and internal task maintenance overhead may
+ * overwhelm processing.
+ *
+ * <p>This class provides {@code adapt} methods for {@link Runnable}
+ * and {@link Callable}, that may be of use when mixing execution of
+ * {@code ForkJoinTasks} with other kinds of tasks. When all tasks are
+ * of this form, consider using a pool constructed in <em>asyncMode</em>.
+ *
+ * <p>ForkJoinTasks are {@code Serializable}, which enables them to be
+ * used in extensions such as remote execution frameworks. It is
+ * sensible to serialize tasks only before or after, but not during,
+ * execution. Serialization is not relied on during execution itself.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
+
+ /*
+ * See the internal documentation of class ForkJoinPool for a
+ * general implementation overview. ForkJoinTasks are mainly
+ * responsible for maintaining their "status" field amidst relays
+ * to methods in ForkJoinWorkerThread and ForkJoinPool.
+ *
+ * The methods of this class are more-or-less layered into
+ * (1) basic status maintenance
+ * (2) execution and awaiting completion
+ * (3) user-level methods that additionally report results.
+ * This is sometimes hard to see because this file orders exported
+ * methods in a way that flows well in javadocs.
+ */
+
+ /*
+ * The status field holds run control status bits packed into a
+ * single int to minimize footprint and to ensure atomicity (via
+ * CAS). Status is initially zero, and takes on nonnegative
+ * values until completed, upon which status holds value
+ * NORMAL, CANCELLED, or EXCEPTIONAL. Tasks undergoing blocking
+ * waits by other threads have the SIGNAL bit set. Completion of
+ * a stolen task with SIGNAL set awakens any waiters via
+ * notifyAll. Even though suboptimal for some purposes, we use
+ * basic builtin wait/notify to take advantage of "monitor
+ * inflation" in JVMs that we would otherwise need to emulate to
+ * avoid adding further per-task bookkeeping overhead. We want
+ * these monitors to be "fat", i.e., not use biasing or thin-lock
+ * techniques, so use some odd coding idioms that tend to avoid
+ * them.
+ */
+
+ /** The run status of this task */
+ volatile int status; // accessed directly by pool and workers
+ private static final int NORMAL = -1;
+ private static final int CANCELLED = -2;
+ private static final int EXCEPTIONAL = -3;
+ private static final int SIGNAL = 1;
+
+ /**
+ * Marks completion and wakes up threads waiting to join this task,
+ * also clearing signal request bits.
+ *
+ * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
+ * @return completion status on exit
+ */
+ private int setCompletion(int completion) {
+ for (int s;;) {
+ if ((s = status) < 0)
+ return s;
+ if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) {
+ if (s != 0)
+ synchronized (this) { notifyAll(); }
+ return completion;
+ }
+ }
+ }
+
+ /**
+ * Tries to block a worker thread until completed or timed out.
+ * Uses Object.wait time argument conventions.
+ * May fail on contention or interrupt.
+ *
+ * @param millis if > 0, wait time.
+ */
+ final void tryAwaitDone(long millis) {
+ int s;
+ try {
+ if (((s = status) > 0 ||
+ (s == 0 &&
+ UNSAFE.compareAndSwapInt(this, statusOffset, 0, SIGNAL))) &&
+ status > 0) {
+ synchronized (this) {
+ if (status > 0)
+ wait(millis);
+ }
+ }
+ } catch (InterruptedException ie) {
+ // caller must check termination
+ }
+ }
+
+ /**
+ * Blocks a non-worker-thread until completion.
+ * @return status upon completion
+ */
+ private int externalAwaitDone() {
+ int s;
+ if ((s = status) >= 0) {
+ boolean interrupted = false;
+ synchronized (this) {
+ while ((s = status) >= 0) {
+ if (s == 0)
+ UNSAFE.compareAndSwapInt(this, statusOffset,
+ 0, SIGNAL);
+ else {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ }
+ }
+ }
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ }
+ return s;
+ }
+
+ /**
+ * Blocks a non-worker-thread until completion or interruption or timeout.
+ */
+ private int externalInterruptibleAwaitDone(long millis)
+ throws InterruptedException {
+ int s;
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ if ((s = status) >= 0) {
+ synchronized (this) {
+ while ((s = status) >= 0) {
+ if (s == 0)
+ UNSAFE.compareAndSwapInt(this, statusOffset,
+ 0, SIGNAL);
+ else {
+ wait(millis);
+ if (millis > 0L)
+ break;
+ }
+ }
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Primary execution method for stolen tasks. Unless done, calls
+ * exec and records status if completed, but doesn't wait for
+ * completion otherwise.
+ */
+ final void doExec() {
+ if (status >= 0) {
+ boolean completed;
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ setExceptionalCompletion(rex);
+ return;
+ }
+ if (completed)
+ setCompletion(NORMAL); // must be outside try block
+ }
+ }
+
+ /**
+ * Primary mechanics for join, get, quietlyJoin.
+ * @return status upon completion
+ */
+ private int doJoin() {
+ Thread t; ForkJoinWorkerThread w; int s; boolean completed;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
+ if ((s = status) < 0)
+ return s;
+ if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) {
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ return setExceptionalCompletion(rex);
+ }
+ if (completed)
+ return setCompletion(NORMAL);
+ }
+ return w.joinTask(this);
+ }
+ else
+ return externalAwaitDone();
+ }
+
+ /**
+ * Primary mechanics for invoke, quietlyInvoke.
+ * @return status upon completion
+ */
+ private int doInvoke() {
+ int s; boolean completed;
+ if ((s = status) < 0)
+ return s;
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ return setExceptionalCompletion(rex);
+ }
+ if (completed)
+ return setCompletion(NORMAL);
+ else
+ return doJoin();
+ }
+
+ // Exception table support
+
+ /**
+ * Table of exceptions thrown by tasks, to enable reporting by
+ * callers. Because exceptions are rare, we don't directly keep
+ * them with task objects, but instead use a weak ref table. Note
+ * that cancellation exceptions don't appear in the table, but are
+ * instead recorded as status values.
+ *
+ * Note: These statics are initialized below in static block.
+ */
+ private static final ExceptionNode[] exceptionTable;
+ private static final ReentrantLock exceptionTableLock;
+ private static final ReferenceQueue<Object> exceptionTableRefQueue;
+
+ /**
+ * Fixed capacity for exceptionTable.
+ */
+ private static final int EXCEPTION_MAP_CAPACITY = 32;
+
+ /**
+ * Key-value nodes for exception table. The chained hash table
+ * uses identity comparisons, full locking, and weak references
+ * for keys. The table has a fixed capacity because it only
+ * maintains task exceptions long enough for joiners to access
+ * them, so should never become very large for sustained
+ * periods. However, since we do not know when the last joiner
+ * completes, we must use weak references and expunge them. We do
+ * so on each operation (hence full locking). Also, some thread in
+ * any ForkJoinPool will call helpExpungeStaleExceptions when its
+ * pool becomes isQuiescent.
+ */
+ static final class ExceptionNode extends WeakReference<ForkJoinTask<?>>{
+ final Throwable ex;
+ ExceptionNode next;
+ final long thrower; // use id not ref to avoid weak cycles
+ ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next) {
+ super(task, exceptionTableRefQueue);
+ this.ex = ex;
+ this.next = next;
+ this.thrower = Thread.currentThread().getId();
+ }
+ }
+
+ /**
+ * Records exception and sets exceptional completion.
+ *
+ * @return status on exit
+ */
+ private int setExceptionalCompletion(Throwable ex) {
+ int h = System.identityHashCode(this);
+ final ReentrantLock lock = exceptionTableLock;
+ lock.lock();
+ try {
+ expungeStaleExceptions();
+ ExceptionNode[] t = exceptionTable;
+ int i = h & (t.length - 1);
+ for (ExceptionNode e = t[i]; ; e = e.next) {
+ if (e == null) {
+ t[i] = new ExceptionNode(this, ex, t[i]);
+ break;
+ }
+ if (e.get() == this) // already present
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ return setCompletion(EXCEPTIONAL);
+ }
+
+ /**
+ * Removes exception node and clears status
+ */
+ private void clearExceptionalCompletion() {
+ int h = System.identityHashCode(this);
+ final ReentrantLock lock = exceptionTableLock;
+ lock.lock();
+ try {
+ ExceptionNode[] t = exceptionTable;
+ int i = h & (t.length - 1);
+ ExceptionNode e = t[i];
+ ExceptionNode pred = null;
+ while (e != null) {
+ ExceptionNode next = e.next;
+ if (e.get() == this) {
+ if (pred == null)
+ t[i] = next;
+ else
+ pred.next = next;
+ break;
+ }
+ pred = e;
+ e = next;
+ }
+ expungeStaleExceptions();
+ status = 0;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns a rethrowable exception for the given task, if
+ * available. To provide accurate stack traces, if the exception
+ * was not thrown by the current thread, we try to create a new
+ * exception of the same type as the one thrown, but with the
+ * recorded exception as its cause. If there is no such
+ * constructor, we instead try to use a no-arg constructor,
+ * followed by initCause, to the same effect. If none of these
+ * apply, or any fail due to other exceptions, we return the
+ * recorded exception, which is still correct, although it may
+ * contain a misleading stack trace.
+ *
+ * @return the exception, or null if none
+ */
+ private Throwable getThrowableException() {
+ if (status != EXCEPTIONAL)
+ return null;
+ int h = System.identityHashCode(this);
+ ExceptionNode e;
+ final ReentrantLock lock = exceptionTableLock;
+ lock.lock();
+ try {
+ expungeStaleExceptions();
+ ExceptionNode[] t = exceptionTable;
+ e = t[h & (t.length - 1)];
+ while (e != null && e.get() != this)
+ e = e.next;
+ } finally {
+ lock.unlock();
+ }
+ Throwable ex;
+ if (e == null || (ex = e.ex) == null)
+ return null;
+ if (e.thrower != Thread.currentThread().getId()) {
+ Class<? extends Throwable> ec = ex.getClass();
+ try {
+ Constructor<?> noArgCtor = null;
+ Constructor<?>[] cs = ec.getConstructors();// public ctors only
+ for (int i = 0; i < cs.length; ++i) {
+ Constructor<?> c = cs[i];
+ Class<?>[] ps = c.getParameterTypes();
+ if (ps.length == 0)
+ noArgCtor = c;
+ else if (ps.length == 1 && ps[0] == Throwable.class)
+ return (Throwable)(c.newInstance(ex));
+ }
+ if (noArgCtor != null) {
+ Throwable wx = (Throwable)(noArgCtor.newInstance());
+ wx.initCause(ex);
+ return wx;
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ return ex;
+ }
+
+ /**
+ * Poll stale refs and remove them. Call only while holding lock.
+ */
+ private static void expungeStaleExceptions() {
+ for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
+ if (x instanceof ExceptionNode) {
+ ForkJoinTask<?> key = ((ExceptionNode)x).get();
+ ExceptionNode[] t = exceptionTable;
+ int i = System.identityHashCode(key) & (t.length - 1);
+ ExceptionNode e = t[i];
+ ExceptionNode pred = null;
+ while (e != null) {
+ ExceptionNode next = e.next;
+ if (e == x) {
+ if (pred == null)
+ t[i] = next;
+ else
+ pred.next = next;
+ break;
+ }
+ pred = e;
+ e = next;
+ }
+ }
+ }
+ }
+
+ /**
+ * If lock is available, poll stale refs and remove them.
+ * Called from ForkJoinPool when pools become quiescent.
+ */
+ static final void helpExpungeStaleExceptions() {
+ final ReentrantLock lock = exceptionTableLock;
+ if (lock.tryLock()) {
+ try {
+ expungeStaleExceptions();
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Report the result of invoke or join; called only upon
+ * non-normal return of internal versions.
+ */
+ private V reportResult() {
+ int s; Throwable ex;
+ if ((s = status) == CANCELLED)
+ throw new CancellationException();
+ if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
+ SneakyThrow.sneakyThrow(ex); // android-changed
+ return getRawResult();
+ }
+
+ // public methods
+
+ /**
+ * Arranges to asynchronously execute this task. While it is not
+ * necessarily enforced, it is a usage error to fork a task more
+ * than once unless it has completed and been reinitialized.
+ * Subsequent modifications to the state of this task or any data
+ * it operates on are not necessarily consistently observable by
+ * any thread other than the one executing it unless preceded by a
+ * call to {@link #join} or related methods, or a call to {@link
+ * #isDone} returning {@code true}.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return {@code this}, to simplify usage
+ */
+ public final ForkJoinTask<V> fork() {
+ ((ForkJoinWorkerThread) Thread.currentThread())
+ .pushTask(this);
+ return this;
+ }
+
+ /**
+ * Returns the result of the computation when it {@link #isDone is
+ * done}. This method differs from {@link #get()} in that
+ * abnormal completion results in {@code RuntimeException} or
+ * {@code Error}, not {@code ExecutionException}, and that
+ * interrupts of the calling thread do <em>not</em> cause the
+ * method to abruptly return by throwing {@code
+ * InterruptedException}.
+ *
+ * @return the computed result
+ */
+ public final V join() {
+ if (doJoin() != NORMAL)
+ return reportResult();
+ else
+ return getRawResult();
+ }
+
+ /**
+ * Commences performing this task, awaits its completion if
+ * necessary, and returns its result, or throws an (unchecked)
+ * {@code RuntimeException} or {@code Error} if the underlying
+ * computation did so.
+ *
+ * @return the computed result
+ */
+ public final V invoke() {
+ if (doInvoke() != NORMAL)
+ return reportResult();
+ else
+ return getRawResult();
+ }
+
+ /**
+ * Forks the given tasks, returning when {@code isDone} holds for
+ * each task or an (unchecked) exception is encountered, in which
+ * case the exception is rethrown. If more than one task
+ * encounters an exception, then this method throws any one of
+ * these exceptions. If any task encounters an exception, the
+ * other may be cancelled. However, the execution status of
+ * individual tasks is not guaranteed upon exceptional return. The
+ * status of each task may be obtained using {@link
+ * #getException()} and related methods to check if they have been
+ * cancelled, completed normally or exceptionally, or left
+ * unprocessed.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @param t1 the first task
+ * @param t2 the second task
+ * @throws NullPointerException if any task is null
+ */
+ public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
+ t2.fork();
+ t1.invoke();
+ t2.join();
+ }
+
+ /**
+ * Forks the given tasks, returning when {@code isDone} holds for
+ * each task or an (unchecked) exception is encountered, in which
+ * case the exception is rethrown. If more than one task
+ * encounters an exception, then this method throws any one of
+ * these exceptions. If any task encounters an exception, others
+ * may be cancelled. However, the execution status of individual
+ * tasks is not guaranteed upon exceptional return. The status of
+ * each task may be obtained using {@link #getException()} and
+ * related methods to check if they have been cancelled, completed
+ * normally or exceptionally, or left unprocessed.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @param tasks the tasks
+ * @throws NullPointerException if any task is null
+ */
+ public static void invokeAll(ForkJoinTask<?>... tasks) {
+ Throwable ex = null;
+ int last = tasks.length - 1;
+ for (int i = last; i >= 0; --i) {
+ ForkJoinTask<?> t = tasks[i];
+ if (t == null) {
+ if (ex == null)
+ ex = new NullPointerException();
+ }
+ else if (i != 0)
+ t.fork();
+ else if (t.doInvoke() < NORMAL && ex == null)
+ ex = t.getException();
+ }
+ for (int i = 1; i <= last; ++i) {
+ ForkJoinTask<?> t = tasks[i];
+ if (t != null) {
+ if (ex != null)
+ t.cancel(false);
+ else if (t.doJoin() < NORMAL)
+ ex = t.getException();
+ }
+ }
+ if (ex != null)
+ SneakyThrow.sneakyThrow(ex); // android-changed
+ }
+
+ /**
+ * Forks all tasks in the specified collection, returning when
+ * {@code isDone} holds for each task or an (unchecked) exception
+ * is encountered, in which case the exception is rethrown. If
+ * more than one task encounters an exception, then this method
+ * throws any one of these exceptions. If any task encounters an
+ * exception, others may be cancelled. However, the execution
+ * status of individual tasks is not guaranteed upon exceptional
+ * return. The status of each task may be obtained using {@link
+ * #getException()} and related methods to check if they have been
+ * cancelled, completed normally or exceptionally, or left
+ * unprocessed.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @param tasks the collection of tasks
+ * @return the tasks argument, to simplify usage
+ * @throws NullPointerException if tasks or any element are null
+ */
+ public static <T extends ForkJoinTask<?>> Collection<T> invokeAll(Collection<T> tasks) {
+ if (!(tasks instanceof RandomAccess) || !(tasks instanceof List<?>)) {
+ invokeAll(tasks.toArray(new ForkJoinTask<?>[tasks.size()]));
+ return tasks;
+ }
+ @SuppressWarnings("unchecked")
+ List<? extends ForkJoinTask<?>> ts =
+ (List<? extends ForkJoinTask<?>>) tasks;
+ Throwable ex = null;
+ int last = ts.size() - 1;
+ for (int i = last; i >= 0; --i) {
+ ForkJoinTask<?> t = ts.get(i);
+ if (t == null) {
+ if (ex == null)
+ ex = new NullPointerException();
+ }
+ else if (i != 0)
+ t.fork();
+ else if (t.doInvoke() < NORMAL && ex == null)
+ ex = t.getException();
+ }
+ for (int i = 1; i <= last; ++i) {
+ ForkJoinTask<?> t = ts.get(i);
+ if (t != null) {
+ if (ex != null)
+ t.cancel(false);
+ else if (t.doJoin() < NORMAL)
+ ex = t.getException();
+ }
+ }
+ if (ex != null)
+ SneakyThrow.sneakyThrow(ex); // android-changed
+ return tasks;
+ }
+
+ /**
+ * Attempts to cancel execution of this task. This attempt will
+ * fail if the task has already completed or could not be
+ * cancelled for some other reason. If successful, and this task
+ * has not started when {@code cancel} is called, execution of
+ * this task is suppressed. After this method returns
+ * successfully, unless there is an intervening call to {@link
+ * #reinitialize}, subsequent calls to {@link #isCancelled},
+ * {@link #isDone}, and {@code cancel} will return {@code true}
+ * and calls to {@link #join} and related methods will result in
+ * {@code CancellationException}.
+ *
+ * <p>This method may be overridden in subclasses, but if so, must
+ * still ensure that these properties hold. In particular, the
+ * {@code cancel} method itself must not throw exceptions.
+ *
+ * <p>This method is designed to be invoked by <em>other</em>
+ * tasks. To terminate the current task, you can just return or
+ * throw an unchecked exception from its computation method, or
+ * invoke {@link #completeExceptionally}.
+ *
+ * @param mayInterruptIfRunning this value has no effect in the
+ * default implementation because interrupts are not used to
+ * control cancellation.
+ *
+ * @return {@code true} if this task is now cancelled
+ */
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return setCompletion(CANCELLED) == CANCELLED;
+ }
+
+ /**
+ * Cancels, ignoring any exceptions thrown by cancel. Used during
+ * worker and pool shutdown. Cancel is spec'ed not to throw any
+ * exceptions, but if it does anyway, we have no recourse during
+ * shutdown, so guard against this case.
+ */
+ final void cancelIgnoringExceptions() {
+ try {
+ cancel(false);
+ } catch (Throwable ignore) {
+ }
+ }
+
+ public final boolean isDone() {
+ return status < 0;
+ }
+
+ public final boolean isCancelled() {
+ return status == CANCELLED;
+ }
+
+ /**
+ * Returns {@code true} if this task threw an exception or was cancelled.
+ *
+ * @return {@code true} if this task threw an exception or was cancelled
+ */
+ public final boolean isCompletedAbnormally() {
+ return status < NORMAL;
+ }
+
+ /**
+ * Returns {@code true} if this task completed without throwing an
+ * exception and was not cancelled.
+ *
+ * @return {@code true} if this task completed without throwing an
+ * exception and was not cancelled
+ */
+ public final boolean isCompletedNormally() {
+ return status == NORMAL;
+ }
+
+ /**
+ * Returns the exception thrown by the base computation, or a
+ * {@code CancellationException} if cancelled, or {@code null} if
+ * none or if the method has not yet completed.
+ *
+ * @return the exception, or {@code null} if none
+ */
+ public final Throwable getException() {
+ int s = status;
+ return ((s >= NORMAL) ? null :
+ (s == CANCELLED) ? new CancellationException() :
+ getThrowableException());
+ }
+
+ /**
+ * Completes this task abnormally, and if not already aborted or
+ * cancelled, causes it to throw the given exception upon
+ * {@code join} and related operations. This method may be used
+ * to induce exceptions in asynchronous tasks, or to force
+ * completion of tasks that would not otherwise complete. Its use
+ * in other situations is discouraged. This method is
+ * overridable, but overridden versions must invoke {@code super}
+ * implementation to maintain guarantees.
+ *
+ * @param ex the exception to throw. If this exception is not a
+ * {@code RuntimeException} or {@code Error}, the actual exception
+ * thrown will be a {@code RuntimeException} with cause {@code ex}.
+ */
+ public void completeExceptionally(Throwable ex) {
+ setExceptionalCompletion((ex instanceof RuntimeException) ||
+ (ex instanceof Error) ? ex :
+ new RuntimeException(ex));
+ }
+
+ /**
+ * Completes this task, and if not already aborted or cancelled,
+ * returning the given value as the result of subsequent
+ * invocations of {@code join} and related operations. This method
+ * may be used to provide results for asynchronous tasks, or to
+ * provide alternative handling for tasks that would not otherwise
+ * complete normally. Its use in other situations is
+ * discouraged. This method is overridable, but overridden
+ * versions must invoke {@code super} implementation to maintain
+ * guarantees.
+ *
+ * @param value the result value for this task
+ */
+ public void complete(V value) {
+ try {
+ setRawResult(value);
+ } catch (Throwable rex) {
+ setExceptionalCompletion(rex);
+ return;
+ }
+ setCompletion(NORMAL);
+ }
+
+ /**
+ * Waits if necessary for the computation to complete, and then
+ * retrieves its result.
+ *
+ * @return the computed result
+ * @throws CancellationException if the computation was cancelled
+ * @throws ExecutionException if the computation threw an
+ * exception
+ * @throws InterruptedException if the current thread is not a
+ * member of a ForkJoinPool and was interrupted while waiting
+ */
+ public final V get() throws InterruptedException, ExecutionException {
+ int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
+ doJoin() : externalInterruptibleAwaitDone(0L);
+ Throwable ex;
+ if (s == CANCELLED)
+ throw new CancellationException();
+ if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
+ throw new ExecutionException(ex);
+ return getRawResult();
+ }
+
+ /**
+ * Waits if necessary for at most the given time for the computation
+ * to complete, and then retrieves its result, if available.
+ *
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the timeout argument
+ * @return the computed result
+ * @throws CancellationException if the computation was cancelled
+ * @throws ExecutionException if the computation threw an
+ * exception
+ * @throws InterruptedException if the current thread is not a
+ * member of a ForkJoinPool and was interrupted while waiting
+ * @throws TimeoutException if the wait timed out
+ */
+ public final V get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ Thread t = Thread.currentThread();
+ if (t instanceof ForkJoinWorkerThread) {
+ ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
+ long nanos = unit.toNanos(timeout);
+ if (status >= 0) {
+ boolean completed = false;
+ if (w.unpushTask(this)) {
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ setExceptionalCompletion(rex);
+ }
+ }
+ if (completed)
+ setCompletion(NORMAL);
+ else if (status >= 0 && nanos > 0)
+ w.pool.timedAwaitJoin(this, nanos);
+ }
+ }
+ else {
+ long millis = unit.toMillis(timeout);
+ if (millis > 0)
+ externalInterruptibleAwaitDone(millis);
+ }
+ int s = status;
+ if (s != NORMAL) {
+ Throwable ex;
+ if (s == CANCELLED)
+ throw new CancellationException();
+ if (s != EXCEPTIONAL)
+ throw new TimeoutException();
+ if ((ex = getThrowableException()) != null)
+ throw new ExecutionException(ex);
+ }
+ return getRawResult();
+ }
+
+ /**
+ * Joins this task, without returning its result or throwing its
+ * exception. This method may be useful when processing
+ * collections of tasks when some have been cancelled or otherwise
+ * known to have aborted.
+ */
+ public final void quietlyJoin() {
+ doJoin();
+ }
+
+ /**
+ * Commences performing this task and awaits its completion if
+ * necessary, without returning its result or throwing its
+ * exception.
+ */
+ public final void quietlyInvoke() {
+ doInvoke();
+ }
+
+ /**
+ * Possibly executes tasks until the pool hosting the current task
+ * {@link ForkJoinPool#isQuiescent is quiescent}. This method may
+ * be of use in designs in which many tasks are forked, but none
+ * are explicitly joined, instead executing them until all are
+ * processed.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ */
+ public static void helpQuiesce() {
+ ((ForkJoinWorkerThread) Thread.currentThread())
+ .helpQuiescePool();
+ }
+
+ /**
+ * Resets the internal bookkeeping state of this task, allowing a
+ * subsequent {@code fork}. This method allows repeated reuse of
+ * this task, but only if reuse occurs when this task has either
+ * never been forked, or has been forked, then completed and all
+ * outstanding joins of this task have also completed. Effects
+ * under any other usage conditions are not guaranteed.
+ * This method may be useful when executing
+ * pre-constructed trees of subtasks in loops.
+ *
+ * <p>Upon completion of this method, {@code isDone()} reports
+ * {@code false}, and {@code getException()} reports {@code
+ * null}. However, the value returned by {@code getRawResult} is
+ * unaffected. To clear this value, you can invoke {@code
+ * setRawResult(null)}.
+ */
+ public void reinitialize() {
+ if (status == EXCEPTIONAL)
+ clearExceptionalCompletion();
+ else
+ status = 0;
+ }
+
+ /**
+ * Returns the pool hosting the current task execution, or null
+ * if this task is executing outside of any ForkJoinPool.
+ *
+ * @see #inForkJoinPool
+ * @return the pool, or {@code null} if none
+ */
+ public static ForkJoinPool getPool() {
+ Thread t = Thread.currentThread();
+ return (t instanceof ForkJoinWorkerThread) ?
+ ((ForkJoinWorkerThread) t).pool : null;
+ }
+
+ /**
+ * Returns {@code true} if the current thread is a {@link
+ * ForkJoinWorkerThread} executing as a ForkJoinPool computation.
+ *
+ * @return {@code true} if the current thread is a {@link
+ * ForkJoinWorkerThread} executing as a ForkJoinPool computation,
+ * or {@code false} otherwise
+ */
+ public static boolean inForkJoinPool() {
+ return Thread.currentThread() instanceof ForkJoinWorkerThread;
+ }
+
+ /**
+ * Tries to unschedule this task for execution. This method will
+ * typically succeed if this task is the most recently forked task
+ * by the current thread, and has not commenced executing in
+ * another thread. This method may be useful when arranging
+ * alternative local processing of tasks that could have been, but
+ * were not, stolen.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return {@code true} if unforked
+ */
+ public boolean tryUnfork() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .unpushTask(this);
+ }
+
+ /**
+ * Returns an estimate of the number of tasks that have been
+ * forked by the current worker thread but not yet executed. This
+ * value may be useful for heuristic decisions about whether to
+ * fork other tasks.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return the number of tasks
+ */
+ public static int getQueuedTaskCount() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .getQueueSize();
+ }
+
+ /**
+ * Returns an estimate of how many more locally queued tasks are
+ * held by the current worker thread than there are other worker
+ * threads that might steal them. This value may be useful for
+ * heuristic decisions about whether to fork other tasks. In many
+ * usages of ForkJoinTasks, at steady state, each worker should
+ * aim to maintain a small constant surplus (for example, 3) of
+ * tasks, and to process computations locally if this threshold is
+ * exceeded.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return the surplus number of tasks, which may be negative
+ */
+ public static int getSurplusQueuedTaskCount() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .getEstimatedSurplusTaskCount();
+ }
+
+ // Extension methods
+
+ /**
+ * Returns the result that would be returned by {@link #join}, even
+ * if this task completed abnormally, or {@code null} if this task
+ * is not known to have been completed. This method is designed
+ * to aid debugging, as well as to support extensions. Its use in
+ * any other context is discouraged.
+ *
+ * @return the result, or {@code null} if not completed
+ */
+ public abstract V getRawResult();
+
+ /**
+ * Forces the given value to be returned as a result. This method
+ * is designed to support extensions, and should not in general be
+ * called otherwise.
+ *
+ * @param value the value
+ */
+ protected abstract void setRawResult(V value);
+
+ /**
+ * Immediately performs the base action of this task. This method
+ * is designed to support extensions, and should not in general be
+ * called otherwise. The return value controls whether this task
+ * is considered to be done normally. It may return false in
+ * asynchronous actions that require explicit invocations of
+ * {@link #complete} to become joinable. It may also throw an
+ * (unchecked) exception to indicate abnormal exit.
+ *
+ * @return {@code true} if completed normally
+ */
+ protected abstract boolean exec();
+
+ /**
+ * Returns, but does not unschedule or execute, a task queued by
+ * the current thread but not yet executed, if one is immediately
+ * available. There is no guarantee that this task will actually
+ * be polled or executed next. Conversely, this method may return
+ * null even if a task exists but cannot be accessed without
+ * contention with other threads. This method is designed
+ * primarily to support extensions, and is unlikely to be useful
+ * otherwise.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return the next task, or {@code null} if none are available
+ */
+ protected static ForkJoinTask<?> peekNextLocalTask() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .peekTask();
+ }
+
+ /**
+ * Unschedules and returns, without executing, the next task
+ * queued by the current thread but not yet executed. This method
+ * is designed primarily to support extensions, and is unlikely to
+ * be useful otherwise.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return the next task, or {@code null} if none are available
+ */
+ protected static ForkJoinTask<?> pollNextLocalTask() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .pollLocalTask();
+ }
+
+ /**
+ * Unschedules and returns, without executing, the next task
+ * queued by the current thread but not yet executed, if one is
+ * available, or if not available, a task that was forked by some
+ * other thread, if available. Availability may be transient, so a
+ * {@code null} result does not necessarily imply quiescence
+ * of the pool this task is operating in. This method is designed
+ * primarily to support extensions, and is unlikely to be useful
+ * otherwise.
+ *
+ * <p>This method may be invoked only from within {@code
+ * ForkJoinPool} computations (as may be determined using method
+ * {@link #inForkJoinPool}). Attempts to invoke in other contexts
+ * result in exceptions or errors, possibly including {@code
+ * ClassCastException}.
+ *
+ * @return a task, or {@code null} if none are available
+ */
+ protected static ForkJoinTask<?> pollTask() {
+ return ((ForkJoinWorkerThread) Thread.currentThread())
+ .pollTask();
+ }
+
+ /**
+ * Adaptor for Runnables. This implements RunnableFuture
+ * to be compliant with AbstractExecutorService constraints
+ * when used in ForkJoinPool.
+ */
+ static final class AdaptedRunnable<T> extends ForkJoinTask<T>
+ implements RunnableFuture<T> {
+ final Runnable runnable;
+ final T resultOnCompletion;
+ T result;
+ AdaptedRunnable(Runnable runnable, T result) {
+ if (runnable == null) throw new NullPointerException();
+ this.runnable = runnable;
+ this.resultOnCompletion = result;
+ }
+ public T getRawResult() { return result; }
+ public void setRawResult(T v) { result = v; }
+ public boolean exec() {
+ runnable.run();
+ result = resultOnCompletion;
+ return true;
+ }
+ public void run() { invoke(); }
+ private static final long serialVersionUID = 5232453952276885070L;
+ }
+
+ /**
+ * Adaptor for Callables
+ */
+ static final class AdaptedCallable<T> extends ForkJoinTask<T>
+ implements RunnableFuture<T> {
+ final Callable<? extends T> callable;
+ T result;
+ AdaptedCallable(Callable<? extends T> callable) {
+ if (callable == null) throw new NullPointerException();
+ this.callable = callable;
+ }
+ public T getRawResult() { return result; }
+ public void setRawResult(T v) { result = v; }
+ public boolean exec() {
+ try {
+ result = callable.call();
+ return true;
+ } catch (Error err) {
+ throw err;
+ } catch (RuntimeException rex) {
+ throw rex;
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ public void run() { invoke(); }
+ private static final long serialVersionUID = 2838392045355241008L;
+ }
+
+ /**
+ * Returns a new {@code ForkJoinTask} that performs the {@code run}
+ * method of the given {@code Runnable} as its action, and returns
+ * a null result upon {@link #join}.
+ *
+ * @param runnable the runnable action
+ * @return the task
+ */
+ public static ForkJoinTask<?> adapt(Runnable runnable) {
+ return new AdaptedRunnable<Void>(runnable, null);
+ }
+
+ /**
+ * Returns a new {@code ForkJoinTask} that performs the {@code run}
+ * method of the given {@code Runnable} as its action, and returns
+ * the given result upon {@link #join}.
+ *
+ * @param runnable the runnable action
+ * @param result the result upon completion
+ * @return the task
+ */
+ public static <T> ForkJoinTask<T> adapt(Runnable runnable, T result) {
+ return new AdaptedRunnable<T>(runnable, result);
+ }
+
+ /**
+ * Returns a new {@code ForkJoinTask} that performs the {@code call}
+ * method of the given {@code Callable} as its action, and returns
+ * its result upon {@link #join}, translating any checked exceptions
+ * encountered into {@code RuntimeException}.
+ *
+ * @param callable the callable action
+ * @return the task
+ */
+ public static <T> ForkJoinTask<T> adapt(Callable<? extends T> callable) {
+ return new AdaptedCallable<T>(callable);
+ }
+
+ // Serialization support
+
+ private static final long serialVersionUID = -7721805057305804111L;
+
+ /**
+ * Saves the state to a stream (that is, serializes it).
+ *
+ * @serialData the current run status and the exception thrown
+ * during execution, or {@code null} if none
+ * @param s the stream
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ s.defaultWriteObject();
+ s.writeObject(getException());
+ }
+
+ /**
+ * Reconstitutes the instance from a stream (that is, deserializes it).
+ *
+ * @param s the stream
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ Object ex = s.readObject();
+ if (ex != null)
+ setExceptionalCompletion((Throwable)ex);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long statusOffset;
+ static {
+ exceptionTableLock = new ReentrantLock();
+ exceptionTableRefQueue = new ReferenceQueue<Object>();
+ exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ statusOffset = UNSAFE.objectFieldOffset
+ (ForkJoinTask.class.getDeclaredField("status"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
new file mode 100644
index 0000000..d99ffe9
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
@@ -0,0 +1,970 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.Collection;
+import java.util.concurrent.RejectedExecutionException;
+import libcore.util.SneakyThrow;
+
+/**
+ * A thread managed by a {@link ForkJoinPool}, which executes
+ * {@link ForkJoinTask}s.
+ * This class is subclassable solely for the sake of adding
+ * functionality -- there are no overridable methods dealing with
+ * scheduling or execution. However, you can override initialization
+ * and termination methods surrounding the main task processing loop.
+ * If you do create such a subclass, you will also need to supply a
+ * custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to use it
+ * in a {@code ForkJoinPool}.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class ForkJoinWorkerThread extends Thread {
+ /*
+ * Overview:
+ *
+ * ForkJoinWorkerThreads are managed by ForkJoinPools and perform
+ * ForkJoinTasks. This class includes bookkeeping in support of
+ * worker activation, suspension, and lifecycle control described
+ * in more detail in the internal documentation of class
+ * ForkJoinPool. And as described further below, this class also
+ * includes special-cased support for some ForkJoinTask
+ * methods. But the main mechanics involve work-stealing:
+ *
+ * Work-stealing queues are special forms of Deques that support
+ * only three of the four possible end-operations -- push, pop,
+ * and deq (aka steal), under the further constraints that push
+ * and pop are called only from the owning thread, while deq may
+ * be called from other threads. (If you are unfamiliar with
+ * them, you probably want to read Herlihy and Shavit's book "The
+ * Art of Multiprocessor programming", chapter 16 describing these
+ * in more detail before proceeding.) The main work-stealing
+ * queue design is roughly similar to those in the papers "Dynamic
+ * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005
+ * (http://research.sun.com/scalable/pubs/index.html) and
+ * "Idempotent work stealing" by Michael, Saraswat, and Vechev,
+ * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
+ * The main differences ultimately stem from gc requirements that
+ * we null out taken slots as soon as we can, to maintain as small
+ * a footprint as possible even in programs generating huge
+ * numbers of tasks. To accomplish this, we shift the CAS
+ * arbitrating pop vs deq (steal) from being on the indices
+ * ("queueBase" and "queueTop") to the slots themselves (mainly
+ * via method "casSlotNull()"). So, both a successful pop and deq
+ * mainly entail a CAS of a slot from non-null to null. Because
+ * we rely on CASes of references, we do not need tag bits on
+ * queueBase or queueTop. They are simple ints as used in any
+ * circular array-based queue (see for example ArrayDeque).
+ * Updates to the indices must still be ordered in a way that
+ * guarantees that queueTop == queueBase means the queue is empty,
+ * but otherwise may err on the side of possibly making the queue
+ * appear nonempty when a push, pop, or deq have not fully
+ * committed. Note that this means that the deq operation,
+ * considered individually, is not wait-free. One thief cannot
+ * successfully continue until another in-progress one (or, if
+ * previously empty, a push) completes. However, in the
+ * aggregate, we ensure at least probabilistic non-blockingness.
+ * If an attempted steal fails, a thief always chooses a different
+ * random victim target to try next. So, in order for one thief to
+ * progress, it suffices for any in-progress deq or new push on
+ * any empty queue to complete.
+ *
+ * This approach also enables support for "async mode" where local
+ * task processing is in FIFO, not LIFO order; simply by using a
+ * version of deq rather than pop when locallyFifo is true (as set
+ * by the ForkJoinPool). This allows use in message-passing
+ * frameworks in which tasks are never joined. However neither
+ * mode considers affinities, loads, cache localities, etc, so
+ * rarely provide the best possible performance on a given
+ * machine, but portably provide good throughput by averaging over
+ * these factors. (Further, even if we did try to use such
+ * information, we do not usually have a basis for exploiting
+ * it. For example, some sets of tasks profit from cache
+ * affinities, but others are harmed by cache pollution effects.)
+ *
+ * When a worker would otherwise be blocked waiting to join a
+ * task, it first tries a form of linear helping: Each worker
+ * records (in field currentSteal) the most recent task it stole
+ * from some other worker. Plus, it records (in field currentJoin)
+ * the task it is currently actively joining. Method joinTask uses
+ * these markers to try to find a worker to help (i.e., steal back
+ * a task from and execute it) that could hasten completion of the
+ * actively joined task. In essence, the joiner executes a task
+ * that would be on its own local deque had the to-be-joined task
+ * not been stolen. This may be seen as a conservative variant of
+ * the approach in Wagner & Calder "Leapfrogging: a portable
+ * technique for implementing efficient futures" SIGPLAN Notices,
+ * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs
+ * in that: (1) We only maintain dependency links across workers
+ * upon steals, rather than use per-task bookkeeping. This may
+ * require a linear scan of workers array to locate stealers, but
+ * usually doesn't because stealers leave hints (that may become
+ * stale/wrong) of where to locate them. This isolates cost to
+ * when it is needed, rather than adding to per-task overhead.
+ * (2) It is "shallow", ignoring nesting and potentially cyclic
+ * mutual steals. (3) It is intentionally racy: field currentJoin
+ * is updated only while actively joining, which means that we
+ * miss links in the chain during long-lived tasks, GC stalls etc
+ * (which is OK since blocking in such cases is usually a good
+ * idea). (4) We bound the number of attempts to find work (see
+ * MAX_HELP) and fall back to suspending the worker and if
+ * necessary replacing it with another.
+ *
+ * Efficient implementation of these algorithms currently relies
+ * on an uncomfortable amount of "Unsafe" mechanics. To maintain
+ * correct orderings, reads and writes of variable queueBase
+ * require volatile ordering. Variable queueTop need not be
+ * volatile because non-local reads always follow those of
+ * queueBase. Similarly, because they are protected by volatile
+ * queueBase reads, reads of the queue array and its slots by
+ * other threads do not need volatile load semantics, but writes
+ * (in push) require store order and CASes (in pop and deq)
+ * require (volatile) CAS semantics. (Michael, Saraswat, and
+ * Vechev's algorithm has similar properties, but without support
+ * for nulling slots.) Since these combinations aren't supported
+ * using ordinary volatiles, the only way to accomplish these
+ * efficiently is to use direct Unsafe calls. (Using external
+ * AtomicIntegers and AtomicReferenceArrays for the indices and
+ * array is significantly slower because of memory locality and
+ * indirection effects.)
+ *
+ * Further, performance on most platforms is very sensitive to
+ * placement and sizing of the (resizable) queue array. Even
+ * though these queues don't usually become all that big, the
+ * initial size must be large enough to counteract cache
+ * contention effects across multiple queues (especially in the
+ * presence of GC cardmarking). Also, to improve thread-locality,
+ * queues are initialized after starting.
+ */
+
+ /**
+ * Mask for pool indices encoded as shorts
+ */
+ private static final int SMASK = 0xffff;
+
+ /**
+ * Capacity of work-stealing queue array upon initialization.
+ * Must be a power of two. Initial size must be at least 4, but is
+ * padded to minimize cache effects.
+ */
+ private static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
+
+ /**
+ * Maximum size for queue array. Must be a power of two
+ * less than or equal to 1 << (31 - width of array entry) to
+ * ensure lack of index wraparound, but is capped at a lower
+ * value to help users trap runaway computations.
+ */
+ private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M
+
+ /**
+ * The work-stealing queue array. Size must be a power of two.
+ * Initialized when started (as opposed to when constructed), to
+ * improve memory locality.
+ */
+ ForkJoinTask<?>[] queue;
+
+ /**
+ * The pool this thread works in. Accessed directly by ForkJoinTask.
+ */
+ final ForkJoinPool pool;
+
+ /**
+ * Index (mod queue.length) of next queue slot to push to or pop
+ * from. It is written only by owner thread, and accessed by other
+ * threads only after reading (volatile) queueBase. Both queueTop
+ * and queueBase are allowed to wrap around on overflow, but
+ * (queueTop - queueBase) still estimates size.
+ */
+ int queueTop;
+
+ /**
+ * Index (mod queue.length) of least valid queue slot, which is
+ * always the next position to steal from if nonempty.
+ */
+ volatile int queueBase;
+
+ /**
+ * The index of most recent stealer, used as a hint to avoid
+ * traversal in method helpJoinTask. This is only a hint because a
+ * worker might have had multiple steals and this only holds one
+ * of them (usually the most current). Declared non-volatile,
+ * relying on other prevailing sync to keep reasonably current.
+ */
+ int stealHint;
+
+ /**
+ * Index of this worker in pool array. Set once by pool before
+ * running, and accessed directly by pool to locate this worker in
+ * its workers array.
+ */
+ final int poolIndex;
+
+ /**
+ * Encoded record for pool task waits. Usages are always
+ * surrounded by volatile reads/writes
+ */
+ int nextWait;
+
+ /**
+ * Complement of poolIndex, offset by count of entries of task
+ * waits. Accessed by ForkJoinPool to manage event waiters.
+ */
+ volatile int eventCount;
+
+ /**
+ * Seed for random number generator for choosing steal victims.
+ * Uses Marsaglia xorshift. Must be initialized as nonzero.
+ */
+ int seed;
+
+ /**
+ * Number of steals. Directly accessed (and reset) by pool when
+ * idle.
+ */
+ int stealCount;
+
+ /**
+ * True if this worker should or did terminate
+ */
+ volatile boolean terminate;
+
+ /**
+ * Set to true before LockSupport.park; false on return
+ */
+ volatile boolean parked;
+
+ /**
+ * True if use local fifo, not default lifo, for local polling.
+ * Shadows value from ForkJoinPool.
+ */
+ final boolean locallyFifo;
+
+ /**
+ * The task most recently stolen from another worker (or
+ * submission queue). All uses are surrounded by enough volatile
+ * reads/writes to maintain as non-volatile.
+ */
+ ForkJoinTask<?> currentSteal;
+
+ /**
+ * The task currently being joined, set only when actively trying
+ * to help other stealers in helpJoinTask. All uses are surrounded
+ * by enough volatile reads/writes to maintain as non-volatile.
+ */
+ ForkJoinTask<?> currentJoin;
+
+ /**
+ * Creates a ForkJoinWorkerThread operating in the given pool.
+ *
+ * @param pool the pool this thread works in
+ * @throws NullPointerException if pool is null
+ */
+ protected ForkJoinWorkerThread(ForkJoinPool pool) {
+ super(pool.nextWorkerName());
+ this.pool = pool;
+ int k = pool.registerWorker(this);
+ poolIndex = k;
+ eventCount = ~k & SMASK; // clear wait count
+ locallyFifo = pool.locallyFifo;
+ Thread.UncaughtExceptionHandler ueh = pool.ueh;
+ if (ueh != null)
+ setUncaughtExceptionHandler(ueh);
+ setDaemon(true);
+ }
+
+ // Public methods
+
+ /**
+ * Returns the pool hosting this thread.
+ *
+ * @return the pool
+ */
+ public ForkJoinPool getPool() {
+ return pool;
+ }
+
+ /**
+ * Returns the index number of this thread in its pool. The
+ * returned value ranges from zero to the maximum number of
+ * threads (minus one) that have ever been created in the pool.
+ * This method may be useful for applications that track status or
+ * collect results per-worker rather than per-task.
+ *
+ * @return the index number
+ */
+ public int getPoolIndex() {
+ return poolIndex;
+ }
+
+ // Randomization
+
+ /**
+ * Computes next value for random victim probes and backoffs.
+ * Scans don't require a very high quality generator, but also not
+ * a crummy one. Marsaglia xor-shift is cheap and works well
+ * enough. Note: This is manually inlined in FJP.scan() to avoid
+ * writes inside busy loops.
+ */
+ private int nextSeed() {
+ int r = seed;
+ r ^= r << 13;
+ r ^= r >>> 17;
+ r ^= r << 5;
+ return seed = r;
+ }
+
+ // Run State management
+
+ /**
+ * Initializes internal state after construction but before
+ * processing any tasks. If you override this method, you must
+ * invoke {@code super.onStart()} at the beginning of the method.
+ * Initialization requires care: Most fields must have legal
+ * default values, to ensure that attempted accesses from other
+ * threads work correctly even before this thread starts
+ * processing tasks.
+ */
+ protected void onStart() {
+ queue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
+ int r = ForkJoinPool.workerSeedGenerator.nextInt();
+ seed = (r == 0) ? 1 : r; // must be nonzero
+ }
+
+ /**
+ * Performs cleanup associated with termination of this worker
+ * thread. If you override this method, you must invoke
+ * {@code super.onTermination} at the end of the overridden method.
+ *
+ * @param exception the exception causing this thread to abort due
+ * to an unrecoverable error, or {@code null} if completed normally
+ */
+ protected void onTermination(Throwable exception) {
+ try {
+ terminate = true;
+ cancelTasks();
+ pool.deregisterWorker(this, exception);
+ } catch (Throwable ex) { // Shouldn't ever happen
+ if (exception == null) // but if so, at least rethrown
+ exception = ex;
+ } finally {
+ if (exception != null)
+ SneakyThrow.sneakyThrow(exception); // android-changed
+ }
+ }
+
+ /**
+ * This method is required to be public, but should never be
+ * called explicitly. It performs the main run loop to execute
+ * {@link ForkJoinTask}s.
+ */
+ public void run() {
+ Throwable exception = null;
+ try {
+ onStart();
+ pool.work(this);
+ } catch (Throwable ex) {
+ exception = ex;
+ } finally {
+ onTermination(exception);
+ }
+ }
+
+ /*
+ * Intrinsics-based atomic writes for queue slots. These are
+ * basically the same as methods in AtomicReferenceArray, but
+ * specialized for (1) ForkJoinTask elements (2) requirement that
+ * nullness and bounds checks have already been performed by
+ * callers and (3) effective offsets are known not to overflow
+ * from int to long (because of MAXIMUM_QUEUE_CAPACITY). We don't
+ * need corresponding version for reads: plain array reads are OK
+ * because they are protected by other volatile reads and are
+ * confirmed by CASes.
+ *
+ * Most uses don't actually call these methods, but instead
+ * contain inlined forms that enable more predictable
+ * optimization. We don't define the version of write used in
+ * pushTask at all, but instead inline there a store-fenced array
+ * slot write.
+ *
+ * Also in most methods, as a performance (not correctness) issue,
+ * we'd like to encourage compilers not to arbitrarily postpone
+ * setting queueTop after writing slot. Currently there is no
+ * intrinsic for arranging this, but using Unsafe putOrderedInt
+ * may be a preferable strategy on some compilers even though its
+ * main effect is a pre-, not post- fence. To simplify possible
+ * changes, the option is left in comments next to the associated
+ * assignments.
+ */
+
+ /**
+ * CASes slot i of array q from t to null. Caller must ensure q is
+ * non-null and index is in range.
+ */
+ private static final boolean casSlotNull(ForkJoinTask<?>[] q, int i,
+ ForkJoinTask<?> t) {
+ return UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null);
+ }
+
+ /**
+ * Performs a volatile write of the given task at given slot of
+ * array q. Caller must ensure q is non-null and index is in
+ * range. This method is used only during resets and backouts.
+ */
+ private static final void writeSlot(ForkJoinTask<?>[] q, int i,
+ ForkJoinTask<?> t) {
+ UNSAFE.putObjectVolatile(q, (i << ASHIFT) + ABASE, t);
+ }
+
+ // queue methods
+
+ /**
+ * Pushes a task. Call only from this thread.
+ *
+ * @param t the task. Caller must ensure non-null.
+ */
+ final void pushTask(ForkJoinTask<?> t) {
+ ForkJoinTask<?>[] q; int s, m;
+ if ((q = queue) != null) { // ignore if queue removed
+ long u = (((s = queueTop) & (m = q.length - 1)) << ASHIFT) + ABASE;
+ UNSAFE.putOrderedObject(q, u, t);
+ queueTop = s + 1; // or use putOrderedInt
+ if ((s -= queueBase) <= 2)
+ pool.signalWork();
+ else if (s == m)
+ growQueue();
+ }
+ }
+
+ /**
+ * Creates or doubles queue array. Transfers elements by
+ * emulating steals (deqs) from old array and placing, oldest
+ * first, into new array.
+ */
+ private void growQueue() {
+ ForkJoinTask<?>[] oldQ = queue;
+ int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY;
+ if (size > MAXIMUM_QUEUE_CAPACITY)
+ throw new RejectedExecutionException("Queue capacity exceeded");
+ if (size < INITIAL_QUEUE_CAPACITY)
+ size = INITIAL_QUEUE_CAPACITY;
+ ForkJoinTask<?>[] q = queue = new ForkJoinTask<?>[size];
+ int mask = size - 1;
+ int top = queueTop;
+ int oldMask;
+ if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) {
+ for (int b = queueBase; b != top; ++b) {
+ long u = ((b & oldMask) << ASHIFT) + ABASE;
+ Object x = UNSAFE.getObjectVolatile(oldQ, u);
+ if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null))
+ UNSAFE.putObjectVolatile
+ (q, ((b & mask) << ASHIFT) + ABASE, x);
+ }
+ }
+ }
+
+ /**
+ * Tries to take a task from the base of the queue, failing if
+ * empty or contended. Note: Specializations of this code appear
+ * in locallyDeqTask and elsewhere.
+ *
+ * @return a task, or null if none or contended
+ */
+ final ForkJoinTask<?> deqTask() {
+ ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
+ if (queueTop != (b = queueBase) &&
+ (q = queue) != null && // must read q after b
+ (i = (q.length - 1) & b) >= 0 &&
+ (t = q[i]) != null && queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null)) {
+ queueBase = b + 1;
+ return t;
+ }
+ return null;
+ }
+
+ /**
+ * Tries to take a task from the base of own queue. Called only
+ * by this thread.
+ *
+ * @return a task, or null if none
+ */
+ final ForkJoinTask<?> locallyDeqTask() {
+ ForkJoinTask<?> t; int m, b, i;
+ ForkJoinTask<?>[] q = queue;
+ if (q != null && (m = q.length - 1) >= 0) {
+ while (queueTop != (b = queueBase)) {
+ if ((t = q[i = m & b]) != null &&
+ queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE,
+ t, null)) {
+ queueBase = b + 1;
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a popped task, or null if empty.
+ * Called only by this thread.
+ */
+ private ForkJoinTask<?> popTask() {
+ int m;
+ ForkJoinTask<?>[] q = queue;
+ if (q != null && (m = q.length - 1) >= 0) {
+ for (int s; (s = queueTop) != queueBase;) {
+ int i = m & --s;
+ long u = (i << ASHIFT) + ABASE; // raw offset
+ ForkJoinTask<?> t = q[i];
+ if (t == null) // lost to stealer
+ break;
+ if (UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ queueTop = s; // or putOrderedInt
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Specialized version of popTask to pop only if topmost element
+ * is the given task. Called only by this thread.
+ *
+ * @param t the task. Caller must ensure non-null.
+ */
+ final boolean unpushTask(ForkJoinTask<?> t) {
+ ForkJoinTask<?>[] q;
+ int s;
+ if ((q = queue) != null && (s = queueTop) != queueBase &&
+ UNSAFE.compareAndSwapObject
+ (q, (((q.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
+ queueTop = s; // or putOrderedInt
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns next task, or null if empty or contended.
+ */
+ final ForkJoinTask<?> peekTask() {
+ int m;
+ ForkJoinTask<?>[] q = queue;
+ if (q == null || (m = q.length - 1) < 0)
+ return null;
+ int i = locallyFifo ? queueBase : (queueTop - 1);
+ return q[i & m];
+ }
+
+ // Support methods for ForkJoinPool
+
+ /**
+ * Runs the given task, plus any local tasks until queue is empty
+ */
+ final void execTask(ForkJoinTask<?> t) {
+ currentSteal = t;
+ for (;;) {
+ if (t != null)
+ t.doExec();
+ if (queueTop == queueBase)
+ break;
+ t = locallyFifo ? locallyDeqTask() : popTask();
+ }
+ ++stealCount;
+ currentSteal = null;
+ }
+
+ /**
+ * Removes and cancels all tasks in queue. Can be called from any
+ * thread.
+ */
+ final void cancelTasks() {
+ ForkJoinTask<?> cj = currentJoin; // try to cancel ongoing tasks
+ if (cj != null && cj.status >= 0)
+ cj.cancelIgnoringExceptions();
+ ForkJoinTask<?> cs = currentSteal;
+ if (cs != null && cs.status >= 0)
+ cs.cancelIgnoringExceptions();
+ while (queueBase != queueTop) {
+ ForkJoinTask<?> t = deqTask();
+ if (t != null)
+ t.cancelIgnoringExceptions();
+ }
+ }
+
+ /**
+ * Drains tasks to given collection c.
+ *
+ * @return the number of tasks drained
+ */
+ final int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
+ int n = 0;
+ while (queueBase != queueTop) {
+ ForkJoinTask<?> t = deqTask();
+ if (t != null) {
+ c.add(t);
+ ++n;
+ }
+ }
+ return n;
+ }
+
+ // Support methods for ForkJoinTask
+
+ /**
+ * Returns an estimate of the number of tasks in the queue.
+ */
+ final int getQueueSize() {
+ return queueTop - queueBase;
+ }
+
+ /**
+ * Gets and removes a local task.
+ *
+ * @return a task, if available
+ */
+ final ForkJoinTask<?> pollLocalTask() {
+ return locallyFifo ? locallyDeqTask() : popTask();
+ }
+
+ /**
+ * Gets and removes a local or stolen task.
+ *
+ * @return a task, if available
+ */
+ final ForkJoinTask<?> pollTask() {
+ ForkJoinWorkerThread[] ws;
+ ForkJoinTask<?> t = pollLocalTask();
+ if (t != null || (ws = pool.workers) == null)
+ return t;
+ int n = ws.length; // cheap version of FJP.scan
+ int steps = n << 1;
+ int r = nextSeed();
+ int i = 0;
+ while (i < steps) {
+ ForkJoinWorkerThread w = ws[(i++ + r) & (n - 1)];
+ if (w != null && w.queueBase != w.queueTop && w.queue != null) {
+ if ((t = w.deqTask()) != null)
+ return t;
+ i = 0;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The maximum stolen->joining link depth allowed in helpJoinTask,
+ * as well as the maximum number of retries (allowing on average
+ * one staleness retry per level) per attempt to instead try
+ * compensation. Depths for legitimate chains are unbounded, but
+ * we use a fixed constant to avoid (otherwise unchecked) cycles
+ * and bound staleness of traversal parameters at the expense of
+ * sometimes blocking when we could be helping.
+ */
+ private static final int MAX_HELP = 16;
+
+ /**
+ * Possibly runs some tasks and/or blocks, until joinMe is done.
+ *
+ * @param joinMe the task to join
+ * @return completion status on exit
+ */
+ final int joinTask(ForkJoinTask<?> joinMe) {
+ ForkJoinTask<?> prevJoin = currentJoin;
+ currentJoin = joinMe;
+ for (int s, retries = MAX_HELP;;) {
+ if ((s = joinMe.status) < 0) {
+ currentJoin = prevJoin;
+ return s;
+ }
+ if (retries > 0) {
+ if (queueTop != queueBase) {
+ if (!localHelpJoinTask(joinMe))
+ retries = 0; // cannot help
+ }
+ else if (retries == MAX_HELP >>> 1) {
+ --retries; // check uncommon case
+ if (tryDeqAndExec(joinMe) >= 0)
+ Thread.yield(); // for politeness
+ }
+ else
+ retries = helpJoinTask(joinMe) ? MAX_HELP : retries - 1;
+ }
+ else {
+ retries = MAX_HELP; // restart if not done
+ pool.tryAwaitJoin(joinMe);
+ }
+ }
+ }
+
+ /**
+ * If present, pops and executes the given task, or any other
+ * cancelled task
+ *
+ * @return false if any other non-cancelled task exists in local queue
+ */
+ private boolean localHelpJoinTask(ForkJoinTask<?> joinMe) {
+ int s, i; ForkJoinTask<?>[] q; ForkJoinTask<?> t;
+ if ((s = queueTop) != queueBase && (q = queue) != null &&
+ (i = (q.length - 1) & --s) >= 0 &&
+ (t = q[i]) != null) {
+ if (t != joinMe && t.status >= 0)
+ return false;
+ if (UNSAFE.compareAndSwapObject
+ (q, (i << ASHIFT) + ABASE, t, null)) {
+ queueTop = s; // or putOrderedInt
+ t.doExec();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tries to locate and execute tasks for a stealer of the given
+ * task, or in turn one of its stealers, Traces
+ * currentSteal->currentJoin links looking for a thread working on
+ * a descendant of the given task and with a non-empty queue to
+ * steal back and execute tasks from. The implementation is very
+ * branchy to cope with potential inconsistencies or loops
+ * encountering chains that are stale, unknown, or of length
+ * greater than MAX_HELP links. All of these cases are dealt with
+ * by just retrying by caller.
+ *
+ * @param joinMe the task to join
+ * @return true if ran a task
+ */
+ private boolean helpJoinTask(ForkJoinTask<?> joinMe) {
+ boolean helped = false;
+ int m = pool.scanGuard & SMASK;
+ ForkJoinWorkerThread[] ws = pool.workers;
+ if (ws != null && ws.length > m && joinMe.status >= 0) {
+ int levels = MAX_HELP; // remaining chain length
+ ForkJoinTask<?> task = joinMe; // base of chain
+ outer:for (ForkJoinWorkerThread thread = this;;) {
+ // Try to find v, the stealer of task, by first using hint
+ ForkJoinWorkerThread v = ws[thread.stealHint & m];
+ if (v == null || v.currentSteal != task) {
+ for (int j = 0; ;) { // search array
+ if ((v = ws[j]) != null && v.currentSteal == task) {
+ thread.stealHint = j;
+ break; // save hint for next time
+ }
+ if (++j > m)
+ break outer; // can't find stealer
+ }
+ }
+ // Try to help v, using specialized form of deqTask
+ for (;;) {
+ ForkJoinTask<?>[] q; int b, i;
+ if (joinMe.status < 0)
+ break outer;
+ if ((b = v.queueBase) == v.queueTop ||
+ (q = v.queue) == null ||
+ (i = (q.length-1) & b) < 0)
+ break; // empty
+ long u = (i << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = q[i];
+ if (task.status < 0)
+ break outer; // stale
+ if (t != null && v.queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ v.queueBase = b + 1;
+ v.stealHint = poolIndex;
+ ForkJoinTask<?> ps = currentSteal;
+ currentSteal = t;
+ t.doExec();
+ currentSteal = ps;
+ helped = true;
+ }
+ }
+ // Try to descend to find v's stealer
+ ForkJoinTask<?> next = v.currentJoin;
+ if (--levels > 0 && task.status >= 0 &&
+ next != null && next != task) {
+ task = next;
+ thread = v;
+ }
+ else
+ break; // max levels, stale, dead-end, or cyclic
+ }
+ }
+ return helped;
+ }
+
+ /**
+ * Performs an uncommon case for joinTask: If task t is at base of
+ * some workers queue, steals and executes it.
+ *
+ * @param t the task
+ * @return t's status
+ */
+ private int tryDeqAndExec(ForkJoinTask<?> t) {
+ int m = pool.scanGuard & SMASK;
+ ForkJoinWorkerThread[] ws = pool.workers;
+ if (ws != null && ws.length > m && t.status >= 0) {
+ for (int j = 0; j <= m; ++j) {
+ ForkJoinTask<?>[] q; int b, i;
+ ForkJoinWorkerThread v = ws[j];
+ if (v != null &&
+ (b = v.queueBase) != v.queueTop &&
+ (q = v.queue) != null &&
+ (i = (q.length - 1) & b) >= 0 &&
+ q[i] == t) {
+ long u = (i << ASHIFT) + ABASE;
+ if (v.queueBase == b &&
+ UNSAFE.compareAndSwapObject(q, u, t, null)) {
+ v.queueBase = b + 1;
+ v.stealHint = poolIndex;
+ ForkJoinTask<?> ps = currentSteal;
+ currentSteal = t;
+ t.doExec();
+ currentSteal = ps;
+ }
+ break;
+ }
+ }
+ }
+ return t.status;
+ }
+
+ /**
+ * Implements ForkJoinTask.getSurplusQueuedTaskCount(). Returns
+ * an estimate of the number of tasks, offset by a function of
+ * number of idle workers.
+ *
+ * This method provides a cheap heuristic guide for task
+ * partitioning when programmers, frameworks, tools, or languages
+ * have little or no idea about task granularity. In essence by
+ * offering this method, we ask users only about tradeoffs in
+ * overhead vs expected throughput and its variance, rather than
+ * how finely to partition tasks.
+ *
+ * In a steady state strict (tree-structured) computation, each
+ * thread makes available for stealing enough tasks for other
+ * threads to remain active. Inductively, if all threads play by
+ * the same rules, each thread should make available only a
+ * constant number of tasks.
+ *
+ * The minimum useful constant is just 1. But using a value of 1
+ * would require immediate replenishment upon each steal to
+ * maintain enough tasks, which is infeasible. Further,
+ * partitionings/granularities of offered tasks should minimize
+ * steal rates, which in general means that threads nearer the top
+ * of computation tree should generate more than those nearer the
+ * bottom. In perfect steady state, each thread is at
+ * approximately the same level of computation tree. However,
+ * producing extra tasks amortizes the uncertainty of progress and
+ * diffusion assumptions.
+ *
+ * So, users will want to use values larger, but not much larger
+ * than 1 to both smooth over transient shortages and hedge
+ * against uneven progress; as traded off against the cost of
+ * extra task overhead. We leave the user to pick a threshold
+ * value to compare with the results of this call to guide
+ * decisions, but recommend values such as 3.
+ *
+ * When all threads are active, it is on average OK to estimate
+ * surplus strictly locally. In steady-state, if one thread is
+ * maintaining say 2 surplus tasks, then so are others. So we can
+ * just use estimated queue length (although note that (queueTop -
+ * queueBase) can be an overestimate because of stealers lagging
+ * increments of queueBase). However, this strategy alone leads
+ * to serious mis-estimates in some non-steady-state conditions
+ * (ramp-up, ramp-down, other stalls). We can detect many of these
+ * by further considering the number of "idle" threads, that are
+ * known to have zero queued tasks, so compensate by a factor of
+ * (#idle/#active) threads.
+ */
+ final int getEstimatedSurplusTaskCount() {
+ return queueTop - queueBase - pool.idlePerActive();
+ }
+
+ /**
+ * Runs tasks until {@code pool.isQuiescent()}. We piggyback on
+ * pool's active count ctl maintenance, but rather than blocking
+ * when tasks cannot be found, we rescan until all others cannot
+ * find tasks either. The bracketing by pool quiescerCounts
+ * updates suppresses pool auto-shutdown mechanics that could
+ * otherwise prematurely terminate the pool because all threads
+ * appear to be inactive.
+ */
+ final void helpQuiescePool() {
+ boolean active = true;
+ ForkJoinTask<?> ps = currentSteal; // to restore below
+ ForkJoinPool p = pool;
+ p.addQuiescerCount(1);
+ for (;;) {
+ ForkJoinWorkerThread[] ws = p.workers;
+ ForkJoinWorkerThread v = null;
+ int n;
+ if (queueTop != queueBase)
+ v = this;
+ else if (ws != null && (n = ws.length) > 1) {
+ ForkJoinWorkerThread w;
+ int r = nextSeed(); // cheap version of FJP.scan
+ int steps = n << 1;
+ for (int i = 0; i < steps; ++i) {
+ if ((w = ws[(i + r) & (n - 1)]) != null &&
+ w.queueBase != w.queueTop) {
+ v = w;
+ break;
+ }
+ }
+ }
+ if (v != null) {
+ ForkJoinTask<?> t;
+ if (!active) {
+ active = true;
+ p.addActiveCount(1);
+ }
+ if ((t = (v != this) ? v.deqTask() :
+ locallyFifo ? locallyDeqTask() : popTask()) != null) {
+ currentSteal = t;
+ t.doExec();
+ currentSteal = ps;
+ }
+ }
+ else {
+ if (active) {
+ active = false;
+ p.addActiveCount(-1);
+ }
+ if (p.isQuiescent()) {
+ p.addActiveCount(1);
+ p.addQuiescerCount(-1);
+ break;
+ }
+ }
+ }
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long ABASE;
+ private static final int ASHIFT;
+
+ static {
+ int s;
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> a = ForkJoinTask[].class;
+ ABASE = UNSAFE.arrayBaseOffset(a);
+ s = UNSAFE.arrayIndexScale(a);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ if ((s & (s-1)) != 0)
+ throw new Error("data type scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/Future.java b/luni/src/main/java/java/util/concurrent/Future.java
index eb84796..6a37729 100644
--- a/luni/src/main/java/java/util/concurrent/Future.java
+++ b/luni/src/main/java/java/util/concurrent/Future.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/FutureTask.java b/luni/src/main/java/java/util/concurrent/FutureTask.java
index 2f2335e..d51881d 100644
--- a/luni/src/main/java/java/util/concurrent/FutureTask.java
+++ b/luni/src/main/java/java/util/concurrent/FutureTask.java
@@ -1,55 +1,116 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.locks.*;
+import java.util.concurrent.locks.LockSupport;
/**
* A cancellable asynchronous computation. This class provides a base
* implementation of {@link Future}, with methods to start and cancel
* a computation, query to see if the computation is complete, and
* retrieve the result of the computation. The result can only be
- * retrieved when the computation has completed; the <tt>get</tt>
- * method will block if the computation has not yet completed. Once
+ * retrieved when the computation has completed; the {@code get}
+ * methods will block if the computation has not yet completed. Once
* the computation has completed, the computation cannot be restarted
- * or cancelled.
+ * or cancelled (unless the computation is invoked using
+ * {@link #runAndReset}).
*
- * <p>A <tt>FutureTask</tt> can be used to wrap a {@link Callable} or
- * {@link java.lang.Runnable} object. Because <tt>FutureTask</tt>
- * implements <tt>Runnable</tt>, a <tt>FutureTask</tt> can be
- * submitted to an {@link Executor} for execution.
+ * <p>A {@code FutureTask} can be used to wrap a {@link Callable} or
+ * {@link Runnable} object. Because {@code FutureTask} implements
+ * {@code Runnable}, a {@code FutureTask} can be submitted to an
+ * {@link Executor} for execution.
*
* <p>In addition to serving as a standalone class, this class provides
- * <tt>protected</tt> functionality that may be useful when creating
+ * {@code protected} functionality that may be useful when creating
* customized task classes.
*
* @since 1.5
* @author Doug Lea
- * @param <V> The result type returned by this FutureTask's <tt>get</tt> method
+ * @param <V> The result type returned by this FutureTask's {@code get} methods
*/
public class FutureTask<V> implements RunnableFuture<V> {
- /** Synchronization control for FutureTask */
- private final Sync sync;
+ /*
+ * Revision notes: This differs from previous versions of this
+ * class that relied on AbstractQueuedSynchronizer, mainly to
+ * avoid surprising users about retaining interrupt status during
+ * cancellation races. Sync control in the current design relies
+ * on a "state" field updated via CAS to track completion, along
+ * with a simple Treiber stack to hold waiting threads.
+ *
+ * Style note: As usual, we bypass overhead of using
+ * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
+ */
/**
- * Creates a <tt>FutureTask</tt> that will, upon running, execute the
- * given <tt>Callable</tt>.
+ * The run state of this task, initially NEW. The run state
+ * transitions to a terminal state only in methods set,
+ * setException, and cancel. During completion, state may take on
+ * transient values of COMPLETING (while outcome is being set) or
+ * INTERRUPTING (only while interrupting the runner to satisfy a
+ * cancel(true)). Transitions from these intermediate to final
+ * states use cheaper ordered/lazy writes because values are unique
+ * and cannot be further modified.
+ *
+ * Possible state transitions:
+ * NEW -> COMPLETING -> NORMAL
+ * NEW -> COMPLETING -> EXCEPTIONAL
+ * NEW -> CANCELLED
+ * NEW -> INTERRUPTING -> INTERRUPTED
+ */
+ private volatile int state;
+ private static final int NEW = 0;
+ private static final int COMPLETING = 1;
+ private static final int NORMAL = 2;
+ private static final int EXCEPTIONAL = 3;
+ private static final int CANCELLED = 4;
+ private static final int INTERRUPTING = 5;
+ private static final int INTERRUPTED = 6;
+
+ /** The underlying callable; nulled out after running */
+ private Callable<V> callable;
+ /** The result to return or exception to throw from get() */
+ private Object outcome; // non-volatile, protected by state reads/writes
+ /** The thread running the callable; CASed during run() */
+ private volatile Thread runner;
+ /** Treiber stack of waiting threads */
+ private volatile WaitNode waiters;
+
+ /**
+ * Returns result or throws exception for completed task.
+ *
+ * @param s completed state value
+ */
+ private V report(int s) throws ExecutionException {
+ Object x = outcome;
+ if (s == NORMAL) {
+ @SuppressWarnings("unchecked") V v = (V)x;
+ return v;
+ }
+ if (s >= CANCELLED)
+ throw new CancellationException();
+ throw new ExecutionException((Throwable)x);
+ }
+
+ /**
+ * Creates a {@code FutureTask} that will, upon running, execute the
+ * given {@code Callable}.
*
* @param callable the callable task
- * @throws NullPointerException if callable is null
+ * @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
- sync = new Sync(callable);
+ this.callable = callable;
+ this.state = NEW; // ensure visibility of callable
}
/**
- * Creates a <tt>FutureTask</tt> that will, upon running, execute the
- * given <tt>Runnable</tt>, and arrange that <tt>get</tt> will return the
+ * Creates a {@code FutureTask} that will, upon running, execute the
+ * given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
@@ -57,29 +118,46 @@
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
- * @throws NullPointerException if runnable is null
+ * @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
- sync = new Sync(Executors.callable(runnable, result));
+ this.callable = Executors.callable(runnable, result);
+ this.state = NEW; // ensure visibility of callable
}
public boolean isCancelled() {
- return sync.innerIsCancelled();
+ return state >= CANCELLED;
}
public boolean isDone() {
- return sync.innerIsDone();
+ return state != NEW;
}
public boolean cancel(boolean mayInterruptIfRunning) {
- return sync.innerCancel(mayInterruptIfRunning);
+ if (state != NEW)
+ return false;
+ if (mayInterruptIfRunning) {
+ if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
+ return false;
+ Thread t = runner;
+ if (t != null)
+ t.interrupt();
+ UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
+ }
+ else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
+ return false;
+ finishCompletion();
+ return true;
}
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
- return sync.innerGet();
+ int s = state;
+ if (s <= COMPLETING)
+ s = awaitDone(false, 0L);
+ return report(s);
}
/**
@@ -87,12 +165,18 @@
*/
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
- return sync.innerGet(unit.toNanos(timeout));
+ if (unit == null)
+ throw new NullPointerException();
+ int s = state;
+ if (s <= COMPLETING &&
+ (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
+ throw new TimeoutException();
+ return report(s);
}
/**
* Protected method invoked when this task transitions to state
- * <tt>isDone</tt> (whether normally or via cancellation). The
+ * {@code isDone} (whether normally or via cancellation). The
* default implementation does nothing. Subclasses may override
* this method to invoke completion callbacks or perform
* bookkeeping. Note that you can query status inside the
@@ -102,230 +186,268 @@
protected void done() { }
/**
- * Sets the result of this Future to the given value unless
+ * Sets the result of this future to the given value unless
* this future has already been set or has been cancelled.
- * This method is invoked internally by the <tt>run</tt> method
+ *
+ * <p>This method is invoked internally by the {@link #run} method
* upon successful completion of the computation.
+ *
* @param v the value
*/
protected void set(V v) {
- sync.innerSet(v);
+ if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
+ outcome = v;
+ UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
+ finishCompletion();
+ }
}
/**
- * Causes this future to report an <tt>ExecutionException</tt>
- * with the given throwable as its cause, unless this Future has
+ * Causes this future to report an {@link ExecutionException}
+ * with the given throwable as its cause, unless this future has
* already been set or has been cancelled.
- * This method is invoked internally by the <tt>run</tt> method
+ *
+ * <p>This method is invoked internally by the {@link #run} method
* upon failure of the computation.
+ *
* @param t the cause of failure
*/
protected void setException(Throwable t) {
- sync.innerSetException(t);
+ if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
+ outcome = t;
+ UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
+ finishCompletion();
+ }
}
- // The following (duplicated) doc comment can be removed once
- //
- // 6270645: Javadoc comments should be inherited from most derived
- // superinterface or superclass
- // is fixed.
- /**
- * Sets this Future to the result of its computation
- * unless it has been cancelled.
- */
public void run() {
- sync.innerRun();
+ if (state != NEW ||
+ !UNSAFE.compareAndSwapObject(this, runnerOffset,
+ null, Thread.currentThread()))
+ return;
+ try {
+ Callable<V> c = callable;
+ if (c != null && state == NEW) {
+ V result;
+ boolean ran;
+ try {
+ result = c.call();
+ ran = true;
+ } catch (Throwable ex) {
+ result = null;
+ ran = false;
+ setException(ex);
+ }
+ if (ran)
+ set(result);
+ }
+ } finally {
+ // runner must be non-null until state is settled to
+ // prevent concurrent calls to run()
+ runner = null;
+ // state must be re-read after nulling runner to prevent
+ // leaked interrupts
+ int s = state;
+ if (s >= INTERRUPTING)
+ handlePossibleCancellationInterrupt(s);
+ }
}
/**
* Executes the computation without setting its result, and then
- * resets this Future to initial state, failing to do so if the
+ * resets this future to initial state, failing to do so if the
* computation encounters an exception or is cancelled. This is
* designed for use with tasks that intrinsically execute more
* than once.
+ *
* @return true if successfully run and reset
*/
protected boolean runAndReset() {
- return sync.innerRunAndReset();
+ if (state != NEW ||
+ !UNSAFE.compareAndSwapObject(this, runnerOffset,
+ null, Thread.currentThread()))
+ return false;
+ boolean ran = false;
+ int s = state;
+ try {
+ Callable<V> c = callable;
+ if (c != null && s == NEW) {
+ try {
+ c.call(); // don't set result
+ ran = true;
+ } catch (Throwable ex) {
+ setException(ex);
+ }
+ }
+ } finally {
+ // runner must be non-null until state is settled to
+ // prevent concurrent calls to run()
+ runner = null;
+ // state must be re-read after nulling runner to prevent
+ // leaked interrupts
+ s = state;
+ if (s >= INTERRUPTING)
+ handlePossibleCancellationInterrupt(s);
+ }
+ return ran && s == NEW;
}
/**
- * Synchronization control for FutureTask. Note that this must be
- * a non-static inner class in order to invoke the protected
- * <tt>done</tt> method. For clarity, all inner class support
- * methods are same as outer, prefixed with "inner".
- *
- * Uses AQS sync state to represent run status
+ * Ensures that any interrupt from a possible cancel(true) is only
+ * delivered to a task while in run or runAndReset.
*/
- private final class Sync extends AbstractQueuedSynchronizer {
- private static final long serialVersionUID = -7828117401763700385L;
+ private void handlePossibleCancellationInterrupt(int s) {
+ // It is possible for our interrupter to stall before getting a
+ // chance to interrupt us. Let's spin-wait patiently.
+ if (s == INTERRUPTING)
+ while (state == INTERRUPTING)
+ Thread.yield(); // wait out pending interrupt
- /** State value representing that task is ready to run */
- private static final int READY = 0;
- /** State value representing that task is running */
- private static final int RUNNING = 1;
- /** State value representing that task ran */
- private static final int RAN = 2;
- /** State value representing that task was cancelled */
- private static final int CANCELLED = 4;
+ // assert state == INTERRUPTED;
- /** The underlying callable */
- private final Callable<V> callable;
- /** The result to return from get() */
- private V result;
- /** The exception to throw from get() */
- private Throwable exception;
+ // We want to clear any interrupt we may have received from
+ // cancel(true). However, it is permissible to use interrupts
+ // as an independent mechanism for a task to communicate with
+ // its caller, and there is no way to clear only the
+ // cancellation interrupt.
+ //
+ // Thread.interrupted();
+ }
- /**
- * The thread running task. When nulled after set/cancel, this
- * indicates that the results are accessible. Must be
- * volatile, to ensure visibility upon completion.
- */
- private volatile Thread runner;
+ /**
+ * Simple linked list nodes to record waiting threads in a Treiber
+ * stack. See other classes such as Phaser and SynchronousQueue
+ * for more detailed explanation.
+ */
+ static final class WaitNode {
+ volatile Thread thread;
+ volatile WaitNode next;
+ WaitNode() { thread = Thread.currentThread(); }
+ }
- Sync(Callable<V> callable) {
- this.callable = callable;
- }
-
- private boolean ranOrCancelled(int state) {
- return (state & (RAN | CANCELLED)) != 0;
- }
-
- /**
- * Implements AQS base acquire to succeed if ran or cancelled
- */
- protected int tryAcquireShared(int ignore) {
- return innerIsDone() ? 1 : -1;
- }
-
- /**
- * Implements AQS base release to always signal after setting
- * final done status by nulling runner thread.
- */
- protected boolean tryReleaseShared(int ignore) {
- runner = null;
- return true;
- }
-
- boolean innerIsCancelled() {
- return getState() == CANCELLED;
- }
-
- boolean innerIsDone() {
- return ranOrCancelled(getState()) && runner == null;
- }
-
- V innerGet() throws InterruptedException, ExecutionException {
- acquireSharedInterruptibly(0);
- if (getState() == CANCELLED)
- throw new CancellationException();
- if (exception != null)
- throw new ExecutionException(exception);
- return result;
- }
-
- V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
- if (!tryAcquireSharedNanos(0, nanosTimeout))
- throw new TimeoutException();
- if (getState() == CANCELLED)
- throw new CancellationException();
- if (exception != null)
- throw new ExecutionException(exception);
- return result;
- }
-
- void innerSet(V v) {
- for (;;) {
- int s = getState();
- if (s == RAN)
- return;
- if (s == CANCELLED) {
- // aggressively release to set runner to null,
- // in case we are racing with a cancel request
- // that will try to interrupt runner
- releaseShared(0);
- return;
+ /**
+ * Removes and signals all waiting threads, invokes done(), and
+ * nulls out callable.
+ */
+ private void finishCompletion() {
+ // assert state > COMPLETING;
+ for (WaitNode q; (q = waiters) != null;) {
+ if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
+ for (;;) {
+ Thread t = q.thread;
+ if (t != null) {
+ q.thread = null;
+ LockSupport.unpark(t);
+ }
+ WaitNode next = q.next;
+ if (next == null)
+ break;
+ q.next = null; // unlink to help gc
+ q = next;
}
- if (compareAndSetState(s, RAN)) {
- result = v;
- releaseShared(0);
- done();
- return;
- }
+ break;
}
}
- void innerSetException(Throwable t) {
- for (;;) {
- int s = getState();
- if (s == RAN)
- return;
- if (s == CANCELLED) {
- // aggressively release to set runner to null,
- // in case we are racing with a cancel request
- // that will try to interrupt runner
- releaseShared(0);
- return;
+ done();
+
+ callable = null; // to reduce footprint
+ }
+
+ /**
+ * Awaits completion or aborts on interrupt or timeout.
+ *
+ * @param timed true if use timed waits
+ * @param nanos time to wait, if timed
+ * @return state upon completion
+ */
+ private int awaitDone(boolean timed, long nanos)
+ throws InterruptedException {
+ long last = timed ? System.nanoTime() : 0L;
+ WaitNode q = null;
+ boolean queued = false;
+ for (;;) {
+ if (Thread.interrupted()) {
+ removeWaiter(q);
+ throw new InterruptedException();
+ }
+
+ int s = state;
+ if (s > COMPLETING) {
+ if (q != null)
+ q.thread = null;
+ return s;
+ }
+ else if (q == null)
+ q = new WaitNode();
+ else if (!queued)
+ queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
+ q.next = waiters, q);
+ else if (timed) {
+ long now = System.nanoTime();
+ if ((nanos -= (now - last)) <= 0L) {
+ removeWaiter(q);
+ return state;
}
- if (compareAndSetState(s, RAN)) {
- exception = t;
- releaseShared(0);
- done();
- return;
+ last = now;
+ LockSupport.parkNanos(this, nanos);
+ }
+ else
+ LockSupport.park(this);
+ }
+ }
+
+ /**
+ * Tries to unlink a timed-out or interrupted wait node to avoid
+ * accumulating garbage. Internal nodes are simply unspliced
+ * without CAS since it is harmless if they are traversed anyway
+ * by releasers. To avoid effects of unsplicing from already
+ * removed nodes, the list is retraversed in case of an apparent
+ * race. This is slow when there are a lot of nodes, but we don't
+ * expect lists to be long enough to outweigh higher-overhead
+ * schemes.
+ */
+ private void removeWaiter(WaitNode node) {
+ if (node != null) {
+ node.thread = null;
+ retry:
+ for (;;) { // restart on removeWaiter race
+ for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
+ s = q.next;
+ if (q.thread != null)
+ pred = q;
+ else if (pred != null) {
+ pred.next = s;
+ if (pred.thread == null) // check for race
+ continue retry;
+ }
+ else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
+ q, s))
+ continue retry;
}
- }
- }
-
- boolean innerCancel(boolean mayInterruptIfRunning) {
- for (;;) {
- int s = getState();
- if (ranOrCancelled(s))
- return false;
- if (compareAndSetState(s, CANCELLED))
- break;
- }
- if (mayInterruptIfRunning) {
- Thread r = runner;
- if (r != null)
- r.interrupt();
- }
- releaseShared(0);
- done();
- return true;
- }
-
- void innerRun() {
- if (!compareAndSetState(READY, RUNNING))
- return;
-
- runner = Thread.currentThread();
- if (getState() == RUNNING) { // recheck after setting thread
- V result;
- try {
- result = callable.call();
- } catch (Throwable ex) {
- setException(ex);
- return;
- }
- set(result);
- } else {
- releaseShared(0); // cancel
- }
- }
-
- boolean innerRunAndReset() {
- if (!compareAndSetState(READY, RUNNING))
- return false;
- try {
- runner = Thread.currentThread();
- if (getState() == RUNNING)
- callable.call(); // don't set result
- runner = null;
- return compareAndSetState(RUNNING, READY);
- } catch (Throwable ex) {
- setException(ex);
- return false;
+ break;
}
}
}
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long stateOffset;
+ private static final long runnerOffset;
+ private static final long waitersOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = FutureTask.class;
+ stateOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("state"));
+ runnerOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("runner"));
+ waitersOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("waiters"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java b/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
index 8c01e71..6f32c47 100644
--- a/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
+++ b/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -13,6 +13,10 @@
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
* An optionally-bounded {@linkplain BlockingDeque blocking deque} based on
* linked nodes.
@@ -34,10 +38,6 @@
* <em>optional</em> methods of the {@link Collection} and {@link
* Iterator} interfaces.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* @since 1.6
* @author Doug Lea
* @param <E> the type of elements held in this collection
@@ -590,7 +590,7 @@
/**
* Inserts the specified element at the end of this deque unless it would
* violate capacity restrictions. When using a capacity-restricted deque,
- * it is generally preferable to use method {@link #offer offer}.
+ * it is generally preferable to use method {@link #offer(Object) offer}.
*
* <p>This method is equivalent to {@link #addLast}.
*
@@ -713,6 +713,8 @@
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
+ if (maxElements <= 0)
+ return 0;
final ReentrantLock lock = this.lock;
lock.lock();
try {
@@ -891,8 +893,7 @@
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -1014,7 +1015,7 @@
/**
* The next node to return in next()
*/
- Node<E> next;
+ Node<E> next;
/**
* nextItem holds on to item fields because once we claim that
@@ -1122,7 +1123,7 @@
}
/**
- * Save the state of this deque to a stream (that is, serialize it).
+ * Saves the state of this deque to a stream (that is, serializes it).
*
* @serialData The capacity (int), followed by elements (each an
* {@code Object}) in the proper order, followed by a null
@@ -1146,8 +1147,8 @@
}
/**
- * Reconstitute this deque from a stream (that is,
- * deserialize it).
+ * Reconstitutes this deque from a stream (that is, deserializes it).
+ *
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
diff --git a/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
index d06c737..e8c9edb 100644
--- a/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -106,13 +106,13 @@
private final int capacity;
/** Current number of elements */
- private final AtomicInteger count = new AtomicInteger(0);
+ private final AtomicInteger count = new AtomicInteger();
/**
* Head of linked list.
* Invariant: head.item == null
*/
- private transient Node<E> head;
+ transient Node<E> head;
/**
* Tail of linked list.
@@ -303,7 +303,7 @@
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
- Node<E> node = new Node(e);
+ Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
@@ -383,7 +383,7 @@
if (count.get() == capacity)
return false;
int c = -1;
- Node<E> node = new Node(e);
+ Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
@@ -601,8 +601,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -699,6 +698,8 @@
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
+ if (maxElements <= 0)
+ return 0;
boolean signalNotFull = false;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
@@ -746,7 +747,7 @@
* @return an iterator over the elements in this queue in proper sequence
*/
public Iterator<E> iterator() {
- return new Itr();
+ return new Itr();
}
private class Itr implements Iterator<E> {
@@ -829,7 +830,7 @@
}
/**
- * Save the state to a stream (that is, serialize it).
+ * Saves the state to a stream (that is, serializes it).
*
* @serialData The capacity is emitted (int), followed by all of
* its elements (each an {@code Object}) in the proper order,
@@ -856,8 +857,7 @@
}
/**
- * Reconstitute this queue instance from a stream (that is,
- * deserialize it).
+ * Reconstitutes this queue from a stream (that is, deserializes it).
*
* @param s the stream
*/
diff --git a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
new file mode 100644
index 0000000..2a3446e
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
@@ -0,0 +1,1323 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.AbstractQueue;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.LockSupport;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
+/**
+ * An unbounded {@link TransferQueue} based on linked nodes.
+ * This queue orders elements FIFO (first-in-first-out) with respect
+ * to any given producer. The <em>head</em> of the queue is that
+ * element that has been on the queue the longest time for some
+ * producer. The <em>tail</em> of the queue is that element that has
+ * been on the queue the shortest time for some producer.
+ *
+ * <p>Beware that, unlike in most collections, the {@code size} method
+ * is <em>NOT</em> a constant-time operation. Because of the
+ * asynchronous nature of these queues, determining the current number
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
+ *
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
+ *
+ * <p>Memory consistency effects: As with other concurrent
+ * collections, actions in a thread prior to placing an object into a
+ * {@code LinkedTransferQueue}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions subsequent to the access or removal of that element from
+ * the {@code LinkedTransferQueue} in another thread.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
+ */
+public class LinkedTransferQueue<E> extends AbstractQueue<E>
+ implements TransferQueue<E>, java.io.Serializable {
+ private static final long serialVersionUID = -3223113410248163686L;
+
+ /*
+ * *** Overview of Dual Queues with Slack ***
+ *
+ * Dual Queues, introduced by Scherer and Scott
+ * (http://www.cs.rice.edu/~wns1/papers/2004-DISC-DDS.pdf) are
+ * (linked) queues in which nodes may represent either data or
+ * requests. When a thread tries to enqueue a data node, but
+ * encounters a request node, it instead "matches" and removes it;
+ * and vice versa for enqueuing requests. Blocking Dual Queues
+ * arrange that threads enqueuing unmatched requests block until
+ * other threads provide the match. Dual Synchronous Queues (see
+ * Scherer, Lea, & Scott
+ * http://www.cs.rochester.edu/u/scott/papers/2009_Scherer_CACM_SSQ.pdf)
+ * additionally arrange that threads enqueuing unmatched data also
+ * block. Dual Transfer Queues support all of these modes, as
+ * dictated by callers.
+ *
+ * A FIFO dual queue may be implemented using a variation of the
+ * Michael & Scott (M&S) lock-free queue algorithm
+ * (http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf).
+ * It maintains two pointer fields, "head", pointing to a
+ * (matched) node that in turn points to the first actual
+ * (unmatched) queue node (or null if empty); and "tail" that
+ * points to the last node on the queue (or again null if
+ * empty). For example, here is a possible queue with four data
+ * elements:
+ *
+ * head tail
+ * | |
+ * v v
+ * M -> U -> U -> U -> U
+ *
+ * The M&S queue algorithm is known to be prone to scalability and
+ * overhead limitations when maintaining (via CAS) these head and
+ * tail pointers. This has led to the development of
+ * contention-reducing variants such as elimination arrays (see
+ * Moir et al http://portal.acm.org/citation.cfm?id=1074013) and
+ * optimistic back pointers (see Ladan-Mozes & Shavit
+ * http://people.csail.mit.edu/edya/publications/OptimisticFIFOQueue-journal.pdf).
+ * However, the nature of dual queues enables a simpler tactic for
+ * improving M&S-style implementations when dual-ness is needed.
+ *
+ * In a dual queue, each node must atomically maintain its match
+ * status. While there are other possible variants, we implement
+ * this here as: for a data-mode node, matching entails CASing an
+ * "item" field from a non-null data value to null upon match, and
+ * vice-versa for request nodes, CASing from null to a data
+ * value. (Note that the linearization properties of this style of
+ * queue are easy to verify -- elements are made available by
+ * linking, and unavailable by matching.) Compared to plain M&S
+ * queues, this property of dual queues requires one additional
+ * successful atomic operation per enq/deq pair. But it also
+ * enables lower cost variants of queue maintenance mechanics. (A
+ * variation of this idea applies even for non-dual queues that
+ * support deletion of interior elements, such as
+ * j.u.c.ConcurrentLinkedQueue.)
+ *
+ * Once a node is matched, its match status can never again
+ * change. We may thus arrange that the linked list of them
+ * contain a prefix of zero or more matched nodes, followed by a
+ * suffix of zero or more unmatched nodes. (Note that we allow
+ * both the prefix and suffix to be zero length, which in turn
+ * means that we do not use a dummy header.) If we were not
+ * concerned with either time or space efficiency, we could
+ * correctly perform enqueue and dequeue operations by traversing
+ * from a pointer to the initial node; CASing the item of the
+ * first unmatched node on match and CASing the next field of the
+ * trailing node on appends. (Plus some special-casing when
+ * initially empty). While this would be a terrible idea in
+ * itself, it does have the benefit of not requiring ANY atomic
+ * updates on head/tail fields.
+ *
+ * We introduce here an approach that lies between the extremes of
+ * never versus always updating queue (head and tail) pointers.
+ * This offers a tradeoff between sometimes requiring extra
+ * traversal steps to locate the first and/or last unmatched
+ * nodes, versus the reduced overhead and contention of fewer
+ * updates to queue pointers. For example, a possible snapshot of
+ * a queue is:
+ *
+ * head tail
+ * | |
+ * v v
+ * M -> M -> U -> U -> U -> U
+ *
+ * The best value for this "slack" (the targeted maximum distance
+ * between the value of "head" and the first unmatched node, and
+ * similarly for "tail") is an empirical matter. We have found
+ * that using very small constants in the range of 1-3 work best
+ * over a range of platforms. Larger values introduce increasing
+ * costs of cache misses and risks of long traversal chains, while
+ * smaller values increase CAS contention and overhead.
+ *
+ * Dual queues with slack differ from plain M&S dual queues by
+ * virtue of only sometimes updating head or tail pointers when
+ * matching, appending, or even traversing nodes; in order to
+ * maintain a targeted slack. The idea of "sometimes" may be
+ * operationalized in several ways. The simplest is to use a
+ * per-operation counter incremented on each traversal step, and
+ * to try (via CAS) to update the associated queue pointer
+ * whenever the count exceeds a threshold. Another, that requires
+ * more overhead, is to use random number generators to update
+ * with a given probability per traversal step.
+ *
+ * In any strategy along these lines, because CASes updating
+ * fields may fail, the actual slack may exceed targeted
+ * slack. However, they may be retried at any time to maintain
+ * targets. Even when using very small slack values, this
+ * approach works well for dual queues because it allows all
+ * operations up to the point of matching or appending an item
+ * (hence potentially allowing progress by another thread) to be
+ * read-only, thus not introducing any further contention. As
+ * described below, we implement this by performing slack
+ * maintenance retries only after these points.
+ *
+ * As an accompaniment to such techniques, traversal overhead can
+ * be further reduced without increasing contention of head
+ * pointer updates: Threads may sometimes shortcut the "next" link
+ * path from the current "head" node to be closer to the currently
+ * known first unmatched node, and similarly for tail. Again, this
+ * may be triggered with using thresholds or randomization.
+ *
+ * These ideas must be further extended to avoid unbounded amounts
+ * of costly-to-reclaim garbage caused by the sequential "next"
+ * links of nodes starting at old forgotten head nodes: As first
+ * described in detail by Boehm
+ * (http://portal.acm.org/citation.cfm?doid=503272.503282) if a GC
+ * delays noticing that any arbitrarily old node has become
+ * garbage, all newer dead nodes will also be unreclaimed.
+ * (Similar issues arise in non-GC environments.) To cope with
+ * this in our implementation, upon CASing to advance the head
+ * pointer, we set the "next" link of the previous head to point
+ * only to itself; thus limiting the length of connected dead lists.
+ * (We also take similar care to wipe out possibly garbage
+ * retaining values held in other Node fields.) However, doing so
+ * adds some further complexity to traversal: If any "next"
+ * pointer links to itself, it indicates that the current thread
+ * has lagged behind a head-update, and so the traversal must
+ * continue from the "head". Traversals trying to find the
+ * current tail starting from "tail" may also encounter
+ * self-links, in which case they also continue at "head".
+ *
+ * It is tempting in slack-based scheme to not even use CAS for
+ * updates (similarly to Ladan-Mozes & Shavit). However, this
+ * cannot be done for head updates under the above link-forgetting
+ * mechanics because an update may leave head at a detached node.
+ * And while direct writes are possible for tail updates, they
+ * increase the risk of long retraversals, and hence long garbage
+ * chains, which can be much more costly than is worthwhile
+ * considering that the cost difference of performing a CAS vs
+ * write is smaller when they are not triggered on each operation
+ * (especially considering that writes and CASes equally require
+ * additional GC bookkeeping ("write barriers") that are sometimes
+ * more costly than the writes themselves because of contention).
+ *
+ * *** Overview of implementation ***
+ *
+ * We use a threshold-based approach to updates, with a slack
+ * threshold of two -- that is, we update head/tail when the
+ * current pointer appears to be two or more steps away from the
+ * first/last node. The slack value is hard-wired: a path greater
+ * than one is naturally implemented by checking equality of
+ * traversal pointers except when the list has only one element,
+ * in which case we keep slack threshold at one. Avoiding tracking
+ * explicit counts across method calls slightly simplifies an
+ * already-messy implementation. Using randomization would
+ * probably work better if there were a low-quality dirt-cheap
+ * per-thread one available, but even ThreadLocalRandom is too
+ * heavy for these purposes.
+ *
+ * With such a small slack threshold value, it is not worthwhile
+ * to augment this with path short-circuiting (i.e., unsplicing
+ * interior nodes) except in the case of cancellation/removal (see
+ * below).
+ *
+ * We allow both the head and tail fields to be null before any
+ * nodes are enqueued; initializing upon first append. This
+ * simplifies some other logic, as well as providing more
+ * efficient explicit control paths instead of letting JVMs insert
+ * implicit NullPointerExceptions when they are null. While not
+ * currently fully implemented, we also leave open the possibility
+ * of re-nulling these fields when empty (which is complicated to
+ * arrange, for little benefit.)
+ *
+ * All enqueue/dequeue operations are handled by the single method
+ * "xfer" with parameters indicating whether to act as some form
+ * of offer, put, poll, take, or transfer (each possibly with
+ * timeout). The relative complexity of using one monolithic
+ * method outweighs the code bulk and maintenance problems of
+ * using separate methods for each case.
+ *
+ * Operation consists of up to three phases. The first is
+ * implemented within method xfer, the second in tryAppend, and
+ * the third in method awaitMatch.
+ *
+ * 1. Try to match an existing node
+ *
+ * Starting at head, skip already-matched nodes until finding
+ * an unmatched node of opposite mode, if one exists, in which
+ * case matching it and returning, also if necessary updating
+ * head to one past the matched node (or the node itself if the
+ * list has no other unmatched nodes). If the CAS misses, then
+ * a loop retries advancing head by two steps until either
+ * success or the slack is at most two. By requiring that each
+ * attempt advances head by two (if applicable), we ensure that
+ * the slack does not grow without bound. Traversals also check
+ * if the initial head is now off-list, in which case they
+ * start at the new head.
+ *
+ * If no candidates are found and the call was untimed
+ * poll/offer, (argument "how" is NOW) return.
+ *
+ * 2. Try to append a new node (method tryAppend)
+ *
+ * Starting at current tail pointer, find the actual last node
+ * and try to append a new node (or if head was null, establish
+ * the first node). Nodes can be appended only if their
+ * predecessors are either already matched or are of the same
+ * mode. If we detect otherwise, then a new node with opposite
+ * mode must have been appended during traversal, so we must
+ * restart at phase 1. The traversal and update steps are
+ * otherwise similar to phase 1: Retrying upon CAS misses and
+ * checking for staleness. In particular, if a self-link is
+ * encountered, then we can safely jump to a node on the list
+ * by continuing the traversal at current head.
+ *
+ * On successful append, if the call was ASYNC, return.
+ *
+ * 3. Await match or cancellation (method awaitMatch)
+ *
+ * Wait for another thread to match node; instead cancelling if
+ * the current thread was interrupted or the wait timed out. On
+ * multiprocessors, we use front-of-queue spinning: If a node
+ * appears to be the first unmatched node in the queue, it
+ * spins a bit before blocking. In either case, before blocking
+ * it tries to unsplice any nodes between the current "head"
+ * and the first unmatched node.
+ *
+ * Front-of-queue spinning vastly improves performance of
+ * heavily contended queues. And so long as it is relatively
+ * brief and "quiet", spinning does not much impact performance
+ * of less-contended queues. During spins threads check their
+ * interrupt status and generate a thread-local random number
+ * to decide to occasionally perform a Thread.yield. While
+ * yield has underdefined specs, we assume that it might help,
+ * and will not hurt, in limiting impact of spinning on busy
+ * systems. We also use smaller (1/2) spins for nodes that are
+ * not known to be front but whose predecessors have not
+ * blocked -- these "chained" spins avoid artifacts of
+ * front-of-queue rules which otherwise lead to alternating
+ * nodes spinning vs blocking. Further, front threads that
+ * represent phase changes (from data to request node or vice
+ * versa) compared to their predecessors receive additional
+ * chained spins, reflecting longer paths typically required to
+ * unblock threads during phase changes.
+ *
+ *
+ * ** Unlinking removed interior nodes **
+ *
+ * In addition to minimizing garbage retention via self-linking
+ * described above, we also unlink removed interior nodes. These
+ * may arise due to timed out or interrupted waits, or calls to
+ * remove(x) or Iterator.remove. Normally, given a node that was
+ * at one time known to be the predecessor of some node s that is
+ * to be removed, we can unsplice s by CASing the next field of
+ * its predecessor if it still points to s (otherwise s must
+ * already have been removed or is now offlist). But there are two
+ * situations in which we cannot guarantee to make node s
+ * unreachable in this way: (1) If s is the trailing node of list
+ * (i.e., with null next), then it is pinned as the target node
+ * for appends, so can only be removed later after other nodes are
+ * appended. (2) We cannot necessarily unlink s given a
+ * predecessor node that is matched (including the case of being
+ * cancelled): the predecessor may already be unspliced, in which
+ * case some previous reachable node may still point to s.
+ * (For further explanation see Herlihy & Shavit "The Art of
+ * Multiprocessor Programming" chapter 9). Although, in both
+ * cases, we can rule out the need for further action if either s
+ * or its predecessor are (or can be made to be) at, or fall off
+ * from, the head of list.
+ *
+ * Without taking these into account, it would be possible for an
+ * unbounded number of supposedly removed nodes to remain
+ * reachable. Situations leading to such buildup are uncommon but
+ * can occur in practice; for example when a series of short timed
+ * calls to poll repeatedly time out but never otherwise fall off
+ * the list because of an untimed call to take at the front of the
+ * queue.
+ *
+ * When these cases arise, rather than always retraversing the
+ * entire list to find an actual predecessor to unlink (which
+ * won't help for case (1) anyway), we record a conservative
+ * estimate of possible unsplice failures (in "sweepVotes").
+ * We trigger a full sweep when the estimate exceeds a threshold
+ * ("SWEEP_THRESHOLD") indicating the maximum number of estimated
+ * removal failures to tolerate before sweeping through, unlinking
+ * cancelled nodes that were not unlinked upon initial removal.
+ * We perform sweeps by the thread hitting threshold (rather than
+ * background threads or by spreading work to other threads)
+ * because in the main contexts in which removal occurs, the
+ * caller is already timed-out, cancelled, or performing a
+ * potentially O(n) operation (e.g. remove(x)), none of which are
+ * time-critical enough to warrant the overhead that alternatives
+ * would impose on other threads.
+ *
+ * Because the sweepVotes estimate is conservative, and because
+ * nodes become unlinked "naturally" as they fall off the head of
+ * the queue, and because we allow votes to accumulate even while
+ * sweeps are in progress, there are typically significantly fewer
+ * such nodes than estimated. Choice of a threshold value
+ * balances the likelihood of wasted effort and contention, versus
+ * providing a worst-case bound on retention of interior nodes in
+ * quiescent queues. The value defined below was chosen
+ * empirically to balance these under various timeout scenarios.
+ *
+ * Note that we cannot self-link unlinked interior nodes during
+ * sweeps. However, the associated garbage chains terminate when
+ * some successor ultimately falls off the head of the list and is
+ * self-linked.
+ */
+
+ /** True if on multiprocessor */
+ private static final boolean MP =
+ Runtime.getRuntime().availableProcessors() > 1;
+
+ /**
+ * The number of times to spin (with randomly interspersed calls
+ * to Thread.yield) on multiprocessor before blocking when a node
+ * is apparently the first waiter in the queue. See above for
+ * explanation. Must be a power of two. The value is empirically
+ * derived -- it works pretty well across a variety of processors,
+ * numbers of CPUs, and OSes.
+ */
+ private static final int FRONT_SPINS = 1 << 7;
+
+ /**
+ * The number of times to spin before blocking when a node is
+ * preceded by another node that is apparently spinning. Also
+ * serves as an increment to FRONT_SPINS on phase changes, and as
+ * base average frequency for yielding during spins. Must be a
+ * power of two.
+ */
+ private static final int CHAINED_SPINS = FRONT_SPINS >>> 1;
+
+ /**
+ * The maximum number of estimated removal failures (sweepVotes)
+ * to tolerate before sweeping through the queue unlinking
+ * cancelled nodes that were not unlinked upon initial
+ * removal. See above for explanation. The value must be at least
+ * two to avoid useless sweeps when removing trailing nodes.
+ */
+ static final int SWEEP_THRESHOLD = 32;
+
+ /**
+ * Queue nodes. Uses Object, not E, for items to allow forgetting
+ * them after use. Relies heavily on Unsafe mechanics to minimize
+ * unnecessary ordering constraints: Writes that are intrinsically
+ * ordered wrt other accesses or CASes use simple relaxed forms.
+ */
+ static final class Node {
+ final boolean isData; // false if this is a request node
+ volatile Object item; // initially non-null if isData; CASed to match
+ volatile Node next;
+ volatile Thread waiter; // null until waiting
+
+ // CAS methods for fields
+ final boolean casNext(Node cmp, Node val) {
+ return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
+ }
+
+ final boolean casItem(Object cmp, Object val) {
+ // assert cmp == null || cmp.getClass() != Node.class;
+ return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
+ }
+
+ /**
+ * Constructs a new node. Uses relaxed write because item can
+ * only be seen after publication via casNext.
+ */
+ Node(Object item, boolean isData) {
+ UNSAFE.putObject(this, itemOffset, item); // relaxed write
+ this.isData = isData;
+ }
+
+ /**
+ * Links node to itself to avoid garbage retention. Called
+ * only after CASing head field, so uses relaxed write.
+ */
+ final void forgetNext() {
+ UNSAFE.putObject(this, nextOffset, this);
+ }
+
+ /**
+ * Sets item to self and waiter to null, to avoid garbage
+ * retention after matching or cancelling. Uses relaxed writes
+ * because order is already constrained in the only calling
+ * contexts: item is forgotten only after volatile/atomic
+ * mechanics that extract items. Similarly, clearing waiter
+ * follows either CAS or return from park (if ever parked;
+ * else we don't care).
+ */
+ final void forgetContents() {
+ UNSAFE.putObject(this, itemOffset, this);
+ UNSAFE.putObject(this, waiterOffset, null);
+ }
+
+ /**
+ * Returns true if this node has been matched, including the
+ * case of artificial matches due to cancellation.
+ */
+ final boolean isMatched() {
+ Object x = item;
+ return (x == this) || ((x == null) == isData);
+ }
+
+ /**
+ * Returns true if this is an unmatched request node.
+ */
+ final boolean isUnmatchedRequest() {
+ return !isData && item == null;
+ }
+
+ /**
+ * Returns true if a node with the given mode cannot be
+ * appended to this node because this node is unmatched and
+ * has opposite data mode.
+ */
+ final boolean cannotPrecede(boolean haveData) {
+ boolean d = isData;
+ Object x;
+ return d != haveData && (x = item) != this && (x != null) == d;
+ }
+
+ /**
+ * Tries to artificially match a data node -- used by remove.
+ */
+ final boolean tryMatchData() {
+ // assert isData;
+ Object x = item;
+ if (x != null && x != this && casItem(x, null)) {
+ LockSupport.unpark(waiter);
+ return true;
+ }
+ return false;
+ }
+
+ private static final long serialVersionUID = -3375979862319811754L;
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long itemOffset;
+ private static final long nextOffset;
+ private static final long waiterOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Node.class;
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ waiterOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("waiter"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+ }
+
+ /** head of the queue; null until first enqueue */
+ transient volatile Node head;
+
+ /** tail of the queue; null until first append */
+ private transient volatile Node tail;
+
+ /** The number of apparent failures to unsplice removed nodes */
+ private transient volatile int sweepVotes;
+
+ // CAS methods for fields
+ private boolean casTail(Node cmp, Node val) {
+ return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
+ }
+
+ private boolean casHead(Node cmp, Node val) {
+ return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
+ }
+
+ private boolean casSweepVotes(int cmp, int val) {
+ return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val);
+ }
+
+ /*
+ * Possible values for "how" argument in xfer method.
+ */
+ private static final int NOW = 0; // for untimed poll, tryTransfer
+ private static final int ASYNC = 1; // for offer, put, add
+ private static final int SYNC = 2; // for transfer, take
+ private static final int TIMED = 3; // for timed poll, tryTransfer
+
+ @SuppressWarnings("unchecked")
+ static <E> E cast(Object item) {
+ // assert item == null || item.getClass() != Node.class;
+ return (E) item;
+ }
+
+ /**
+ * Implements all queuing methods. See above for explanation.
+ *
+ * @param e the item or null for take
+ * @param haveData true if this is a put, else a take
+ * @param how NOW, ASYNC, SYNC, or TIMED
+ * @param nanos timeout in nanosecs, used only if mode is TIMED
+ * @return an item if matched, else e
+ * @throws NullPointerException if haveData mode but e is null
+ */
+ private E xfer(E e, boolean haveData, int how, long nanos) {
+ if (haveData && (e == null))
+ throw new NullPointerException();
+ Node s = null; // the node to append, if needed
+
+ retry:
+ for (;;) { // restart on append race
+
+ for (Node h = head, p = h; p != null;) { // find & match first node
+ boolean isData = p.isData;
+ Object item = p.item;
+ if (item != p && (item != null) == isData) { // unmatched
+ if (isData == haveData) // can't match
+ break;
+ if (p.casItem(item, e)) { // match
+ for (Node q = p; q != h;) {
+ Node n = q.next; // update by 2 unless singleton
+ if (head == h && casHead(h, n == null ? q : n)) {
+ h.forgetNext();
+ break;
+ } // advance and retry
+ if ((h = head) == null ||
+ (q = h.next) == null || !q.isMatched())
+ break; // unless slack < 2
+ }
+ LockSupport.unpark(p.waiter);
+ return LinkedTransferQueue.<E>cast(item);
+ }
+ }
+ Node n = p.next;
+ p = (p != n) ? n : (h = head); // Use head if p offlist
+ }
+
+ if (how != NOW) { // No matches available
+ if (s == null)
+ s = new Node(e, haveData);
+ Node pred = tryAppend(s, haveData);
+ if (pred == null)
+ continue retry; // lost race vs opposite mode
+ if (how != ASYNC)
+ return awaitMatch(s, pred, e, (how == TIMED), nanos);
+ }
+ return e; // not waiting
+ }
+ }
+
+ /**
+ * Tries to append node s as tail.
+ *
+ * @param s the node to append
+ * @param haveData true if appending in data mode
+ * @return null on failure due to losing race with append in
+ * different mode, else s's predecessor, or s itself if no
+ * predecessor
+ */
+ private Node tryAppend(Node s, boolean haveData) {
+ for (Node t = tail, p = t;;) { // move p to last node and append
+ Node n, u; // temps for reads of next & tail
+ if (p == null && (p = head) == null) {
+ if (casHead(null, s))
+ return s; // initialize
+ }
+ else if (p.cannotPrecede(haveData))
+ return null; // lost race vs opposite mode
+ else if ((n = p.next) != null) // not last; keep traversing
+ p = p != t && t != (u = tail) ? (t = u) : // stale tail
+ (p != n) ? n : null; // restart if off list
+ else if (!p.casNext(null, s))
+ p = p.next; // re-read on CAS failure
+ else {
+ if (p != t) { // update if slack now >= 2
+ while ((tail != t || !casTail(t, s)) &&
+ (t = tail) != null &&
+ (s = t.next) != null && // advance and retry
+ (s = s.next) != null && s != t);
+ }
+ return p;
+ }
+ }
+ }
+
+ /**
+ * Spins/yields/blocks until node s is matched or caller gives up.
+ *
+ * @param s the waiting node
+ * @param pred the predecessor of s, or s itself if it has no
+ * predecessor, or null if unknown (the null case does not occur
+ * in any current calls but may in possible future extensions)
+ * @param e the comparison value for checking match
+ * @param timed if true, wait only until timeout elapses
+ * @param nanos timeout in nanosecs, used only if timed is true
+ * @return matched item, or e if unmatched on interrupt or timeout
+ */
+ private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) {
+ long lastTime = timed ? System.nanoTime() : 0L;
+ Thread w = Thread.currentThread();
+ int spins = -1; // initialized after first item and cancel checks
+ ThreadLocalRandom randomYields = null; // bound if needed
+
+ for (;;) {
+ Object item = s.item;
+ if (item != e) { // matched
+ // assert item != s;
+ s.forgetContents(); // avoid garbage
+ return LinkedTransferQueue.<E>cast(item);
+ }
+ if ((w.isInterrupted() || (timed && nanos <= 0)) &&
+ s.casItem(e, s)) { // cancel
+ unsplice(pred, s);
+ return e;
+ }
+
+ if (spins < 0) { // establish spins at/near front
+ if ((spins = spinsFor(pred, s.isData)) > 0)
+ randomYields = ThreadLocalRandom.current();
+ }
+ else if (spins > 0) { // spin
+ --spins;
+ if (randomYields.nextInt(CHAINED_SPINS) == 0)
+ Thread.yield(); // occasionally yield
+ }
+ else if (s.waiter == null) {
+ s.waiter = w; // request unpark then recheck
+ }
+ else if (timed) {
+ long now = System.nanoTime();
+ if ((nanos -= now - lastTime) > 0)
+ LockSupport.parkNanos(this, nanos);
+ lastTime = now;
+ }
+ else {
+ LockSupport.park(this);
+ }
+ }
+ }
+
+ /**
+ * Returns spin/yield value for a node with given predecessor and
+ * data mode. See above for explanation.
+ */
+ private static int spinsFor(Node pred, boolean haveData) {
+ if (MP && pred != null) {
+ if (pred.isData != haveData) // phase change
+ return FRONT_SPINS + CHAINED_SPINS;
+ if (pred.isMatched()) // probably at front
+ return FRONT_SPINS;
+ if (pred.waiter == null) // pred apparently spinning
+ return CHAINED_SPINS;
+ }
+ return 0;
+ }
+
+ /* -------------- Traversal methods -------------- */
+
+ /**
+ * Returns the successor of p, or the head node if p.next has been
+ * linked to self, which will only be true if traversing with a
+ * stale pointer that is now off the list.
+ */
+ final Node succ(Node p) {
+ Node next = p.next;
+ return (p == next) ? head : next;
+ }
+
+ /**
+ * Returns the first unmatched node of the given mode, or null if
+ * none. Used by methods isEmpty, hasWaitingConsumer.
+ */
+ private Node firstOfMode(boolean isData) {
+ for (Node p = head; p != null; p = succ(p)) {
+ if (!p.isMatched())
+ return (p.isData == isData) ? p : null;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the item in the first unmatched node with isData; or
+ * null if none. Used by peek.
+ */
+ private E firstDataItem() {
+ for (Node p = head; p != null; p = succ(p)) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p)
+ return LinkedTransferQueue.<E>cast(item);
+ }
+ else if (item == null)
+ return null;
+ }
+ return null;
+ }
+
+ /**
+ * Traverses and counts unmatched nodes of the given mode.
+ * Used by methods size and getWaitingConsumerCount.
+ */
+ private int countOfMode(boolean data) {
+ int count = 0;
+ for (Node p = head; p != null; ) {
+ if (!p.isMatched()) {
+ if (p.isData != data)
+ return 0;
+ if (++count == Integer.MAX_VALUE) // saturated
+ break;
+ }
+ Node n = p.next;
+ if (n != p)
+ p = n;
+ else {
+ count = 0;
+ p = head;
+ }
+ }
+ return count;
+ }
+
+ final class Itr implements Iterator<E> {
+ private Node nextNode; // next node to return item for
+ private E nextItem; // the corresponding item
+ private Node lastRet; // last returned node, to support remove
+ private Node lastPred; // predecessor to unlink lastRet
+
+ /**
+ * Moves to next node after prev, or first node if prev null.
+ */
+ private void advance(Node prev) {
+ /*
+ * To track and avoid buildup of deleted nodes in the face
+ * of calls to both Queue.remove and Itr.remove, we must
+ * include variants of unsplice and sweep upon each
+ * advance: Upon Itr.remove, we may need to catch up links
+ * from lastPred, and upon other removes, we might need to
+ * skip ahead from stale nodes and unsplice deleted ones
+ * found while advancing.
+ */
+
+ Node r, b; // reset lastPred upon possible deletion of lastRet
+ if ((r = lastRet) != null && !r.isMatched())
+ lastPred = r; // next lastPred is old lastRet
+ else if ((b = lastPred) == null || b.isMatched())
+ lastPred = null; // at start of list
+ else {
+ Node s, n; // help with removal of lastPred.next
+ while ((s = b.next) != null &&
+ s != b && s.isMatched() &&
+ (n = s.next) != null && n != s)
+ b.casNext(s, n);
+ }
+
+ this.lastRet = prev;
+
+ for (Node p = prev, s, n;;) {
+ s = (p == null) ? head : p.next;
+ if (s == null)
+ break;
+ else if (s == p) {
+ p = null;
+ continue;
+ }
+ Object item = s.item;
+ if (s.isData) {
+ if (item != null && item != s) {
+ nextItem = LinkedTransferQueue.<E>cast(item);
+ nextNode = s;
+ return;
+ }
+ }
+ else if (item == null)
+ break;
+ // assert s.isMatched();
+ if (p == null)
+ p = s;
+ else if ((n = s.next) == null)
+ break;
+ else if (s == n)
+ p = null;
+ else
+ p.casNext(s, n);
+ }
+ nextNode = null;
+ nextItem = null;
+ }
+
+ Itr() {
+ advance(null);
+ }
+
+ public final boolean hasNext() {
+ return nextNode != null;
+ }
+
+ public final E next() {
+ Node p = nextNode;
+ if (p == null) throw new NoSuchElementException();
+ E e = nextItem;
+ advance(p);
+ return e;
+ }
+
+ public final void remove() {
+ final Node lastRet = this.lastRet;
+ if (lastRet == null)
+ throw new IllegalStateException();
+ this.lastRet = null;
+ if (lastRet.tryMatchData())
+ unsplice(lastPred, lastRet);
+ }
+ }
+
+ /* -------------- Removal methods -------------- */
+
+ /**
+ * Unsplices (now or later) the given deleted/cancelled node with
+ * the given predecessor.
+ *
+ * @param pred a node that was at one time known to be the
+ * predecessor of s, or null or s itself if s is/was at head
+ * @param s the node to be unspliced
+ */
+ final void unsplice(Node pred, Node s) {
+ s.forgetContents(); // forget unneeded fields
+ /*
+ * See above for rationale. Briefly: if pred still points to
+ * s, try to unlink s. If s cannot be unlinked, because it is
+ * trailing node or pred might be unlinked, and neither pred
+ * nor s are head or offlist, add to sweepVotes, and if enough
+ * votes have accumulated, sweep.
+ */
+ if (pred != null && pred != s && pred.next == s) {
+ Node n = s.next;
+ if (n == null ||
+ (n != s && pred.casNext(s, n) && pred.isMatched())) {
+ for (;;) { // check if at, or could be, head
+ Node h = head;
+ if (h == pred || h == s || h == null)
+ return; // at head or list empty
+ if (!h.isMatched())
+ break;
+ Node hn = h.next;
+ if (hn == null)
+ return; // now empty
+ if (hn != h && casHead(h, hn))
+ h.forgetNext(); // advance head
+ }
+ if (pred.next != pred && s.next != s) { // recheck if offlist
+ for (;;) { // sweep now if enough votes
+ int v = sweepVotes;
+ if (v < SWEEP_THRESHOLD) {
+ if (casSweepVotes(v, v + 1))
+ break;
+ }
+ else if (casSweepVotes(v, 0)) {
+ sweep();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Unlinks matched (typically cancelled) nodes encountered in a
+ * traversal from head.
+ */
+ private void sweep() {
+ for (Node p = head, s, n; p != null && (s = p.next) != null; ) {
+ if (!s.isMatched())
+ // Unmatched nodes are never self-linked
+ p = s;
+ else if ((n = s.next) == null) // trailing node is pinned
+ break;
+ else if (s == n) // stale
+ // No need to also check for p == s, since that implies s == n
+ p = head;
+ else
+ p.casNext(s, n);
+ }
+ }
+
+ /**
+ * Main implementation of remove(Object)
+ */
+ private boolean findAndRemove(Object e) {
+ if (e != null) {
+ for (Node pred = null, p = head; p != null; ) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p && e.equals(item) &&
+ p.tryMatchData()) {
+ unsplice(pred, p);
+ return true;
+ }
+ }
+ else if (item == null)
+ break;
+ pred = p;
+ if ((p = p.next) == pred) { // stale
+ pred = null;
+ p = head;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Creates an initially empty {@code LinkedTransferQueue}.
+ */
+ public LinkedTransferQueue() {
+ }
+
+ /**
+ * Creates a {@code LinkedTransferQueue}
+ * initially containing the elements of the given collection,
+ * added in traversal order of the collection's iterator.
+ *
+ * @param c the collection of elements to initially contain
+ * @throws NullPointerException if the specified collection or any
+ * of its elements are null
+ */
+ public LinkedTransferQueue(Collection<? extends E> c) {
+ this();
+ addAll(c);
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
+ * As the queue is unbounded, this method will never block.
+ *
+ * @throws NullPointerException if the specified element is null
+ */
+ public void put(E e) {
+ xfer(e, true, ASYNC, 0);
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
+ * As the queue is unbounded, this method will never block or
+ * return {@code false}.
+ *
+ * @return {@code true} (as specified by
+ * {@link java.util.concurrent.BlockingQueue#offer(Object,long,TimeUnit)
+ * BlockingQueue.offer})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offer(E e, long timeout, TimeUnit unit) {
+ xfer(e, true, ASYNC, 0);
+ return true;
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
+ * As the queue is unbounded, this method will never return {@code false}.
+ *
+ * @return {@code true} (as specified by {@link Queue#offer})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offer(E e) {
+ xfer(e, true, ASYNC, 0);
+ return true;
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
+ * As the queue is unbounded, this method will never throw
+ * {@link IllegalStateException} or return {@code false}.
+ *
+ * @return {@code true} (as specified by {@link Collection#add})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean add(E e) {
+ xfer(e, true, ASYNC, 0);
+ return true;
+ }
+
+ /**
+ * Transfers the element to a waiting consumer immediately, if possible.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * otherwise returning {@code false} without enqueuing the element.
+ *
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean tryTransfer(E e) {
+ return xfer(e, true, NOW, 0) == null;
+ }
+
+ /**
+ * Transfers the element to a consumer, waiting if necessary to do so.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * else inserts the specified element at the tail of this queue
+ * and waits until the element is received by a consumer.
+ *
+ * @throws NullPointerException if the specified element is null
+ */
+ public void transfer(E e) throws InterruptedException {
+ if (xfer(e, true, SYNC, 0) != null) {
+ Thread.interrupted(); // failure possible only due to interrupt
+ throw new InterruptedException();
+ }
+ }
+
+ /**
+ * Transfers the element to a consumer if it is possible to do so
+ * before the timeout elapses.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * else inserts the specified element at the tail of this queue
+ * and waits until the element is received by a consumer,
+ * returning {@code false} if the specified wait time elapses
+ * before the element can be transferred.
+ *
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean tryTransfer(E e, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ if (xfer(e, true, TIMED, unit.toNanos(timeout)) == null)
+ return true;
+ if (!Thread.interrupted())
+ return false;
+ throw new InterruptedException();
+ }
+
+ public E take() throws InterruptedException {
+ E e = xfer(null, false, SYNC, 0);
+ if (e != null)
+ return e;
+ Thread.interrupted();
+ throw new InterruptedException();
+ }
+
+ public E poll(long timeout, TimeUnit unit) throws InterruptedException {
+ E e = xfer(null, false, TIMED, unit.toNanos(timeout));
+ if (e != null || !Thread.interrupted())
+ return e;
+ throw new InterruptedException();
+ }
+
+ public E poll() {
+ return xfer(null, false, NOW, 0);
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ public int drainTo(Collection<? super E> c) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ int n = 0;
+ for (E e; (e = poll()) != null;) {
+ c.add(e);
+ ++n;
+ }
+ return n;
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ public int drainTo(Collection<? super E> c, int maxElements) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ int n = 0;
+ for (E e; n < maxElements && (e = poll()) != null;) {
+ c.add(e);
+ ++n;
+ }
+ return n;
+ }
+
+ /**
+ * Returns an iterator over the elements in this queue in proper sequence.
+ * The elements will be returned in order from first (head) to last (tail).
+ *
+ * <p>The returned iterator is a "weakly consistent" iterator that
+ * will never throw {@link java.util.ConcurrentModificationException
+ * ConcurrentModificationException}, and guarantees to traverse
+ * elements as they existed upon construction of the iterator, and
+ * may (but is not guaranteed to) reflect any modifications
+ * subsequent to construction.
+ *
+ * @return an iterator over the elements in this queue in proper sequence
+ */
+ public Iterator<E> iterator() {
+ return new Itr();
+ }
+
+ public E peek() {
+ return firstDataItem();
+ }
+
+ /**
+ * Returns {@code true} if this queue contains no elements.
+ *
+ * @return {@code true} if this queue contains no elements
+ */
+ public boolean isEmpty() {
+ for (Node p = head; p != null; p = succ(p)) {
+ if (!p.isMatched())
+ return !p.isData;
+ }
+ return true;
+ }
+
+ public boolean hasWaitingConsumer() {
+ return firstOfMode(false) != null;
+ }
+
+ /**
+ * Returns the number of elements in this queue. If this queue
+ * contains more than {@code Integer.MAX_VALUE} elements, returns
+ * {@code Integer.MAX_VALUE}.
+ *
+ * <p>Beware that, unlike in most collections, this method is
+ * <em>NOT</em> a constant-time operation. Because of the
+ * asynchronous nature of these queues, determining the current
+ * number of elements requires an O(n) traversal.
+ *
+ * @return the number of elements in this queue
+ */
+ public int size() {
+ return countOfMode(true);
+ }
+
+ public int getWaitingConsumerCount() {
+ return countOfMode(false);
+ }
+
+ /**
+ * Removes a single instance of the specified element from this queue,
+ * if it is present. More formally, removes an element {@code e} such
+ * that {@code o.equals(e)}, if this queue contains one or more such
+ * elements.
+ * Returns {@code true} if this queue contained the specified element
+ * (or equivalently, if this queue changed as a result of the call).
+ *
+ * @param o element to be removed from this queue, if present
+ * @return {@code true} if this queue changed as a result of the call
+ */
+ public boolean remove(Object o) {
+ return findAndRemove(o);
+ }
+
+ /**
+ * Returns {@code true} if this queue contains the specified element.
+ * More formally, returns {@code true} if and only if this queue contains
+ * at least one element {@code e} such that {@code o.equals(e)}.
+ *
+ * @param o object to be checked for containment in this queue
+ * @return {@code true} if this queue contains the specified element
+ */
+ public boolean contains(Object o) {
+ if (o == null) return false;
+ for (Node p = head; p != null; p = succ(p)) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p && o.equals(item))
+ return true;
+ }
+ else if (item == null)
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Always returns {@code Integer.MAX_VALUE} because a
+ * {@code LinkedTransferQueue} is not capacity constrained.
+ *
+ * @return {@code Integer.MAX_VALUE} (as specified by
+ * {@link java.util.concurrent.BlockingQueue#remainingCapacity()
+ * BlockingQueue.remainingCapacity})
+ */
+ public int remainingCapacity() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Saves the state to a stream (that is, serializes it).
+ *
+ * @serialData All of the elements (each an {@code E}) in
+ * the proper order, followed by a null
+ * @param s the stream
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ s.defaultWriteObject();
+ for (E e : this)
+ s.writeObject(e);
+ // Use trailing null as sentinel
+ s.writeObject(null);
+ }
+
+ /**
+ * Reconstitutes the Queue instance from a stream (that is,
+ * deserializes it).
+ *
+ * @param s the stream
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ for (;;) {
+ @SuppressWarnings("unchecked") E item = (E) s.readObject();
+ if (item == null)
+ break;
+ else
+ offer(item);
+ }
+ }
+
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ private static final long tailOffset;
+ private static final long sweepVotesOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = LinkedTransferQueue.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ sweepVotesOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("sweepVotes"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/Phaser.java b/luni/src/main/java/java/util/concurrent/Phaser.java
new file mode 100644
index 0000000..25ff743
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/Phaser.java
@@ -0,0 +1,1135 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.LockSupport;
+
+/**
+ * A reusable synchronization barrier, similar in functionality to
+ * {@link java.util.concurrent.CyclicBarrier CyclicBarrier} and
+ * {@link java.util.concurrent.CountDownLatch CountDownLatch}
+ * but supporting more flexible usage.
+ *
+ * <p> <b>Registration.</b> Unlike the case for other barriers, the
+ * number of parties <em>registered</em> to synchronize on a phaser
+ * may vary over time. Tasks may be registered at any time (using
+ * methods {@link #register}, {@link #bulkRegister}, or forms of
+ * constructors establishing initial numbers of parties), and
+ * optionally deregistered upon any arrival (using {@link
+ * #arriveAndDeregister}). As is the case with most basic
+ * synchronization constructs, registration and deregistration affect
+ * only internal counts; they do not establish any further internal
+ * bookkeeping, so tasks cannot query whether they are registered.
+ * (However, you can introduce such bookkeeping by subclassing this
+ * class.)
+ *
+ * <p> <b>Synchronization.</b> Like a {@code CyclicBarrier}, a {@code
+ * Phaser} may be repeatedly awaited. Method {@link
+ * #arriveAndAwaitAdvance} has effect analogous to {@link
+ * java.util.concurrent.CyclicBarrier#await CyclicBarrier.await}. Each
+ * generation of a phaser has an associated phase number. The phase
+ * number starts at zero, and advances when all parties arrive at the
+ * phaser, wrapping around to zero after reaching {@code
+ * Integer.MAX_VALUE}. The use of phase numbers enables independent
+ * control of actions upon arrival at a phaser and upon awaiting
+ * others, via two kinds of methods that may be invoked by any
+ * registered party:
+ *
+ * <ul>
+ *
+ * <li> <b>Arrival.</b> Methods {@link #arrive} and
+ * {@link #arriveAndDeregister} record arrival. These methods
+ * do not block, but return an associated <em>arrival phase
+ * number</em>; that is, the phase number of the phaser to which
+ * the arrival applied. When the final party for a given phase
+ * arrives, an optional action is performed and the phase
+ * advances. These actions are performed by the party
+ * triggering a phase advance, and are arranged by overriding
+ * method {@link #onAdvance(int, int)}, which also controls
+ * termination. Overriding this method is similar to, but more
+ * flexible than, providing a barrier action to a {@code
+ * CyclicBarrier}.
+ *
+ * <li> <b>Waiting.</b> Method {@link #awaitAdvance} requires an
+ * argument indicating an arrival phase number, and returns when
+ * the phaser advances to (or is already at) a different phase.
+ * Unlike similar constructions using {@code CyclicBarrier},
+ * method {@code awaitAdvance} continues to wait even if the
+ * waiting thread is interrupted. Interruptible and timeout
+ * versions are also available, but exceptions encountered while
+ * tasks wait interruptibly or with timeout do not change the
+ * state of the phaser. If necessary, you can perform any
+ * associated recovery within handlers of those exceptions,
+ * often after invoking {@code forceTermination}. Phasers may
+ * also be used by tasks executing in a {@link ForkJoinPool},
+ * which will ensure sufficient parallelism to execute tasks
+ * when others are blocked waiting for a phase to advance.
+ *
+ * </ul>
+ *
+ * <p> <b>Termination.</b> A phaser may enter a <em>termination</em>
+ * state, that may be checked using method {@link #isTerminated}. Upon
+ * termination, all synchronization methods immediately return without
+ * waiting for advance, as indicated by a negative return value.
+ * Similarly, attempts to register upon termination have no effect.
+ * Termination is triggered when an invocation of {@code onAdvance}
+ * returns {@code true}. The default implementation returns {@code
+ * true} if a deregistration has caused the number of registered
+ * parties to become zero. As illustrated below, when phasers control
+ * actions with a fixed number of iterations, it is often convenient
+ * to override this method to cause termination when the current phase
+ * number reaches a threshold. Method {@link #forceTermination} is
+ * also available to abruptly release waiting threads and allow them
+ * to terminate.
+ *
+ * <p> <b>Tiering.</b> Phasers may be <em>tiered</em> (i.e.,
+ * constructed in tree structures) to reduce contention. Phasers with
+ * large numbers of parties that would otherwise experience heavy
+ * synchronization contention costs may instead be set up so that
+ * groups of sub-phasers share a common parent. This may greatly
+ * increase throughput even though it incurs greater per-operation
+ * overhead.
+ *
+ * <p>In a tree of tiered phasers, registration and deregistration of
+ * child phasers with their parent are managed automatically.
+ * Whenever the number of registered parties of a child phaser becomes
+ * non-zero (as established in the {@link #Phaser(Phaser,int)}
+ * constructor, {@link #register}, or {@link #bulkRegister}), the
+ * child phaser is registered with its parent. Whenever the number of
+ * registered parties becomes zero as the result of an invocation of
+ * {@link #arriveAndDeregister}, the child phaser is deregistered
+ * from its parent.
+ *
+ * <p><b>Monitoring.</b> While synchronization methods may be invoked
+ * only by registered parties, the current state of a phaser may be
+ * monitored by any caller. At any given moment there are {@link
+ * #getRegisteredParties} parties in total, of which {@link
+ * #getArrivedParties} have arrived at the current phase ({@link
+ * #getPhase}). When the remaining ({@link #getUnarrivedParties})
+ * parties arrive, the phase advances. The values returned by these
+ * methods may reflect transient states and so are not in general
+ * useful for synchronization control. Method {@link #toString}
+ * returns snapshots of these state queries in a form convenient for
+ * informal monitoring.
+ *
+ * <p><b>Sample usages:</b>
+ *
+ * <p>A {@code Phaser} may be used instead of a {@code CountDownLatch}
+ * to control a one-shot action serving a variable number of parties.
+ * The typical idiom is for the method setting this up to first
+ * register, then start the actions, then deregister, as in:
+ *
+ * <pre> {@code
+ * void runTasks(List<Runnable> tasks) {
+ * final Phaser phaser = new Phaser(1); // "1" to register self
+ * // create and start threads
+ * for (final Runnable task : tasks) {
+ * phaser.register();
+ * new Thread() {
+ * public void run() {
+ * phaser.arriveAndAwaitAdvance(); // await all creation
+ * task.run();
+ * }
+ * }.start();
+ * }
+ *
+ * // allow threads to start and deregister self
+ * phaser.arriveAndDeregister();
+ * }}</pre>
+ *
+ * <p>One way to cause a set of threads to repeatedly perform actions
+ * for a given number of iterations is to override {@code onAdvance}:
+ *
+ * <pre> {@code
+ * void startTasks(List<Runnable> tasks, final int iterations) {
+ * final Phaser phaser = new Phaser() {
+ * protected boolean onAdvance(int phase, int registeredParties) {
+ * return phase >= iterations || registeredParties == 0;
+ * }
+ * };
+ * phaser.register();
+ * for (final Runnable task : tasks) {
+ * phaser.register();
+ * new Thread() {
+ * public void run() {
+ * do {
+ * task.run();
+ * phaser.arriveAndAwaitAdvance();
+ * } while (!phaser.isTerminated());
+ * }
+ * }.start();
+ * }
+ * phaser.arriveAndDeregister(); // deregister self, don't wait
+ * }}</pre>
+ *
+ * If the main task must later await termination, it
+ * may re-register and then execute a similar loop:
+ * <pre> {@code
+ * // ...
+ * phaser.register();
+ * while (!phaser.isTerminated())
+ * phaser.arriveAndAwaitAdvance();}</pre>
+ *
+ * <p>Related constructions may be used to await particular phase numbers
+ * in contexts where you are sure that the phase will never wrap around
+ * {@code Integer.MAX_VALUE}. For example:
+ *
+ * <pre> {@code
+ * void awaitPhase(Phaser phaser, int phase) {
+ * int p = phaser.register(); // assumes caller not already registered
+ * while (p < phase) {
+ * if (phaser.isTerminated())
+ * // ... deal with unexpected termination
+ * else
+ * p = phaser.arriveAndAwaitAdvance();
+ * }
+ * phaser.arriveAndDeregister();
+ * }}</pre>
+ *
+ *
+ * <p>To create a set of {@code n} tasks using a tree of phasers, you
+ * could use code of the following form, assuming a Task class with a
+ * constructor accepting a {@code Phaser} that it registers with upon
+ * construction. After invocation of {@code build(new Task[n], 0, n,
+ * new Phaser())}, these tasks could then be started, for example by
+ * submitting to a pool:
+ *
+ * <pre> {@code
+ * void build(Task[] tasks, int lo, int hi, Phaser ph) {
+ * if (hi - lo > TASKS_PER_PHASER) {
+ * for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
+ * int j = Math.min(i + TASKS_PER_PHASER, hi);
+ * build(tasks, i, j, new Phaser(ph));
+ * }
+ * } else {
+ * for (int i = lo; i < hi; ++i)
+ * tasks[i] = new Task(ph);
+ * // assumes new Task(ph) performs ph.register()
+ * }
+ * }}</pre>
+ *
+ * The best value of {@code TASKS_PER_PHASER} depends mainly on
+ * expected synchronization rates. A value as low as four may
+ * be appropriate for extremely small per-phase task bodies (thus
+ * high rates), or up to hundreds for extremely large ones.
+ *
+ * <p><b>Implementation notes</b>: This implementation restricts the
+ * maximum number of parties to 65535. Attempts to register additional
+ * parties result in {@code IllegalStateException}. However, you can and
+ * should create tiered phasers to accommodate arbitrarily large sets
+ * of participants.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class Phaser {
+ /*
+ * This class implements an extension of X10 "clocks". Thanks to
+ * Vijay Saraswat for the idea, and to Vivek Sarkar for
+ * enhancements to extend functionality.
+ */
+
+ /**
+ * Primary state representation, holding four bit-fields:
+ *
+ * unarrived -- the number of parties yet to hit barrier (bits 0-15)
+ * parties -- the number of parties to wait (bits 16-31)
+ * phase -- the generation of the barrier (bits 32-62)
+ * terminated -- set if barrier is terminated (bit 63 / sign)
+ *
+ * Except that a phaser with no registered parties is
+ * distinguished by the otherwise illegal state of having zero
+ * parties and one unarrived parties (encoded as EMPTY below).
+ *
+ * To efficiently maintain atomicity, these values are packed into
+ * a single (atomic) long. Good performance relies on keeping
+ * state decoding and encoding simple, and keeping race windows
+ * short.
+ *
+ * All state updates are performed via CAS except initial
+ * registration of a sub-phaser (i.e., one with a non-null
+ * parent). In this (relatively rare) case, we use built-in
+ * synchronization to lock while first registering with its
+ * parent.
+ *
+ * The phase of a subphaser is allowed to lag that of its
+ * ancestors until it is actually accessed -- see method
+ * reconcileState.
+ */
+ private volatile long state;
+
+ private static final int MAX_PARTIES = 0xffff;
+ private static final int MAX_PHASE = Integer.MAX_VALUE;
+ private static final int PARTIES_SHIFT = 16;
+ private static final int PHASE_SHIFT = 32;
+ private static final int UNARRIVED_MASK = 0xffff; // to mask ints
+ private static final long PARTIES_MASK = 0xffff0000L; // to mask longs
+ private static final long COUNTS_MASK = 0xffffffffL;
+ private static final long TERMINATION_BIT = 1L << 63;
+
+ // some special values
+ private static final int ONE_ARRIVAL = 1;
+ private static final int ONE_PARTY = 1 << PARTIES_SHIFT;
+ private static final int ONE_DEREGISTER = ONE_ARRIVAL|ONE_PARTY;
+ private static final int EMPTY = 1;
+
+ // The following unpacking methods are usually manually inlined
+
+ private static int unarrivedOf(long s) {
+ int counts = (int)s;
+ return (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
+ }
+
+ private static int partiesOf(long s) {
+ return (int)s >>> PARTIES_SHIFT;
+ }
+
+ private static int phaseOf(long s) {
+ return (int)(s >>> PHASE_SHIFT);
+ }
+
+ private static int arrivedOf(long s) {
+ int counts = (int)s;
+ return (counts == EMPTY) ? 0 :
+ (counts >>> PARTIES_SHIFT) - (counts & UNARRIVED_MASK);
+ }
+
+ /**
+ * The parent of this phaser, or null if none
+ */
+ private final Phaser parent;
+
+ /**
+ * The root of phaser tree. Equals this if not in a tree.
+ */
+ private final Phaser root;
+
+ /**
+ * Heads of Treiber stacks for waiting threads. To eliminate
+ * contention when releasing some threads while adding others, we
+ * use two of them, alternating across even and odd phases.
+ * Subphasers share queues with root to speed up releases.
+ */
+ private final AtomicReference<QNode> evenQ;
+ private final AtomicReference<QNode> oddQ;
+
+ private AtomicReference<QNode> queueFor(int phase) {
+ return ((phase & 1) == 0) ? evenQ : oddQ;
+ }
+
+ /**
+ * Returns message string for bounds exceptions on arrival.
+ */
+ private String badArrive(long s) {
+ return "Attempted arrival of unregistered party for " +
+ stateToString(s);
+ }
+
+ /**
+ * Returns message string for bounds exceptions on registration.
+ */
+ private String badRegister(long s) {
+ return "Attempt to register more than " +
+ MAX_PARTIES + " parties for " + stateToString(s);
+ }
+
+ /**
+ * Main implementation for methods arrive and arriveAndDeregister.
+ * Manually tuned to speed up and minimize race windows for the
+ * common case of just decrementing unarrived field.
+ *
+ * @param adjust value to subtract from state;
+ * ONE_ARRIVAL for arrive,
+ * ONE_DEREGISTER for arriveAndDeregister
+ */
+ private int doArrive(int adjust) {
+ final Phaser root = this.root;
+ for (;;) {
+ long s = (root == this) ? state : reconcileState();
+ int phase = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ int counts = (int)s;
+ int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
+ if (unarrived <= 0)
+ throw new IllegalStateException(badArrive(s));
+ if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adjust)) {
+ if (unarrived == 1) {
+ long n = s & PARTIES_MASK; // base of next state
+ int nextUnarrived = (int)n >>> PARTIES_SHIFT;
+ if (root == this) {
+ if (onAdvance(phase, nextUnarrived))
+ n |= TERMINATION_BIT;
+ else if (nextUnarrived == 0)
+ n |= EMPTY;
+ else
+ n |= nextUnarrived;
+ int nextPhase = (phase + 1) & MAX_PHASE;
+ n |= (long)nextPhase << PHASE_SHIFT;
+ UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
+ releaseWaiters(phase);
+ }
+ else if (nextUnarrived == 0) { // propagate deregistration
+ phase = parent.doArrive(ONE_DEREGISTER);
+ UNSAFE.compareAndSwapLong(this, stateOffset,
+ s, s | EMPTY);
+ }
+ else
+ phase = parent.doArrive(ONE_ARRIVAL);
+ }
+ return phase;
+ }
+ }
+ }
+
+ /**
+ * Implementation of register, bulkRegister
+ *
+ * @param registrations number to add to both parties and
+ * unarrived fields. Must be greater than zero.
+ */
+ private int doRegister(int registrations) {
+ // adjustment to state
+ long adjust = ((long)registrations << PARTIES_SHIFT) | registrations;
+ final Phaser parent = this.parent;
+ int phase;
+ for (;;) {
+ long s = (parent == null) ? state : reconcileState();
+ int counts = (int)s;
+ int parties = counts >>> PARTIES_SHIFT;
+ int unarrived = counts & UNARRIVED_MASK;
+ if (registrations > MAX_PARTIES - parties)
+ throw new IllegalStateException(badRegister(s));
+ phase = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ break;
+ if (counts != EMPTY) { // not 1st registration
+ if (parent == null || reconcileState() == s) {
+ if (unarrived == 0) // wait out advance
+ root.internalAwaitAdvance(phase, null);
+ else if (UNSAFE.compareAndSwapLong(this, stateOffset,
+ s, s + adjust))
+ break;
+ }
+ }
+ else if (parent == null) { // 1st root registration
+ long next = ((long)phase << PHASE_SHIFT) | adjust;
+ if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))
+ break;
+ }
+ else {
+ synchronized (this) { // 1st sub registration
+ if (state == s) { // recheck under lock
+ phase = parent.doRegister(1);
+ if (phase < 0)
+ break;
+ // finish registration whenever parent registration
+ // succeeded, even when racing with termination,
+ // since these are part of the same "transaction".
+ while (!UNSAFE.compareAndSwapLong
+ (this, stateOffset, s,
+ ((long)phase << PHASE_SHIFT) | adjust)) {
+ s = state;
+ phase = (int)(root.state >>> PHASE_SHIFT);
+ // assert (int)s == EMPTY;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return phase;
+ }
+
+ /**
+ * Resolves lagged phase propagation from root if necessary.
+ * Reconciliation normally occurs when root has advanced but
+ * subphasers have not yet done so, in which case they must finish
+ * their own advance by setting unarrived to parties (or if
+ * parties is zero, resetting to unregistered EMPTY state).
+ *
+ * @return reconciled state
+ */
+ private long reconcileState() {
+ final Phaser root = this.root;
+ long s = state;
+ if (root != this) {
+ int phase, p;
+ // CAS to root phase with current parties, tripping unarrived
+ while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
+ (int)(s >>> PHASE_SHIFT) &&
+ !UNSAFE.compareAndSwapLong
+ (this, stateOffset, s,
+ s = (((long)phase << PHASE_SHIFT) |
+ ((phase < 0) ? (s & COUNTS_MASK) :
+ (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
+ ((s & PARTIES_MASK) | p))))))
+ s = state;
+ }
+ return s;
+ }
+
+ /**
+ * Creates a new phaser with no initially registered parties, no
+ * parent, and initial phase number 0. Any thread using this
+ * phaser will need to first register for it.
+ */
+ public Phaser() {
+ this(null, 0);
+ }
+
+ /**
+ * Creates a new phaser with the given number of registered
+ * unarrived parties, no parent, and initial phase number 0.
+ *
+ * @param parties the number of parties required to advance to the
+ * next phase
+ * @throws IllegalArgumentException if parties less than zero
+ * or greater than the maximum number of parties supported
+ */
+ public Phaser(int parties) {
+ this(null, parties);
+ }
+
+ /**
+ * Equivalent to {@link #Phaser(Phaser, int) Phaser(parent, 0)}.
+ *
+ * @param parent the parent phaser
+ */
+ public Phaser(Phaser parent) {
+ this(parent, 0);
+ }
+
+ /**
+ * Creates a new phaser with the given parent and number of
+ * registered unarrived parties. When the given parent is non-null
+ * and the given number of parties is greater than zero, this
+ * child phaser is registered with its parent.
+ *
+ * @param parent the parent phaser
+ * @param parties the number of parties required to advance to the
+ * next phase
+ * @throws IllegalArgumentException if parties less than zero
+ * or greater than the maximum number of parties supported
+ */
+ public Phaser(Phaser parent, int parties) {
+ if (parties >>> PARTIES_SHIFT != 0)
+ throw new IllegalArgumentException("Illegal number of parties");
+ int phase = 0;
+ this.parent = parent;
+ if (parent != null) {
+ final Phaser root = parent.root;
+ this.root = root;
+ this.evenQ = root.evenQ;
+ this.oddQ = root.oddQ;
+ if (parties != 0)
+ phase = parent.doRegister(1);
+ }
+ else {
+ this.root = this;
+ this.evenQ = new AtomicReference<QNode>();
+ this.oddQ = new AtomicReference<QNode>();
+ }
+ this.state = (parties == 0) ? (long)EMPTY :
+ ((long)phase << PHASE_SHIFT) |
+ ((long)parties << PARTIES_SHIFT) |
+ ((long)parties);
+ }
+
+ /**
+ * Adds a new unarrived party to this phaser. If an ongoing
+ * invocation of {@link #onAdvance} is in progress, this method
+ * may await its completion before returning. If this phaser has
+ * a parent, and this phaser previously had no registered parties,
+ * this child phaser is also registered with its parent. If
+ * this phaser is terminated, the attempt to register has
+ * no effect, and a negative value is returned.
+ *
+ * @return the arrival phase number to which this registration
+ * applied. If this value is negative, then this phaser has
+ * terminated, in which case registration has no effect.
+ * @throws IllegalStateException if attempting to register more
+ * than the maximum supported number of parties
+ */
+ public int register() {
+ return doRegister(1);
+ }
+
+ /**
+ * Adds the given number of new unarrived parties to this phaser.
+ * If an ongoing invocation of {@link #onAdvance} is in progress,
+ * this method may await its completion before returning. If this
+ * phaser has a parent, and the given number of parties is greater
+ * than zero, and this phaser previously had no registered
+ * parties, this child phaser is also registered with its parent.
+ * If this phaser is terminated, the attempt to register has no
+ * effect, and a negative value is returned.
+ *
+ * @param parties the number of additional parties required to
+ * advance to the next phase
+ * @return the arrival phase number to which this registration
+ * applied. If this value is negative, then this phaser has
+ * terminated, in which case registration has no effect.
+ * @throws IllegalStateException if attempting to register more
+ * than the maximum supported number of parties
+ * @throws IllegalArgumentException if {@code parties < 0}
+ */
+ public int bulkRegister(int parties) {
+ if (parties < 0)
+ throw new IllegalArgumentException();
+ if (parties == 0)
+ return getPhase();
+ return doRegister(parties);
+ }
+
+ /**
+ * Arrives at this phaser, without waiting for others to arrive.
+ *
+ * <p>It is a usage error for an unregistered party to invoke this
+ * method. However, this error may result in an {@code
+ * IllegalStateException} only upon some subsequent operation on
+ * this phaser, if ever.
+ *
+ * @return the arrival phase number, or a negative value if terminated
+ * @throws IllegalStateException if not terminated and the number
+ * of unarrived parties would become negative
+ */
+ public int arrive() {
+ return doArrive(ONE_ARRIVAL);
+ }
+
+ /**
+ * Arrives at this phaser and deregisters from it without waiting
+ * for others to arrive. Deregistration reduces the number of
+ * parties required to advance in future phases. If this phaser
+ * has a parent, and deregistration causes this phaser to have
+ * zero parties, this phaser is also deregistered from its parent.
+ *
+ * <p>It is a usage error for an unregistered party to invoke this
+ * method. However, this error may result in an {@code
+ * IllegalStateException} only upon some subsequent operation on
+ * this phaser, if ever.
+ *
+ * @return the arrival phase number, or a negative value if terminated
+ * @throws IllegalStateException if not terminated and the number
+ * of registered or unarrived parties would become negative
+ */
+ public int arriveAndDeregister() {
+ return doArrive(ONE_DEREGISTER);
+ }
+
+ /**
+ * Arrives at this phaser and awaits others. Equivalent in effect
+ * to {@code awaitAdvance(arrive())}. If you need to await with
+ * interruption or timeout, you can arrange this with an analogous
+ * construction using one of the other forms of the {@code
+ * awaitAdvance} method. If instead you need to deregister upon
+ * arrival, use {@code awaitAdvance(arriveAndDeregister())}.
+ *
+ * <p>It is a usage error for an unregistered party to invoke this
+ * method. However, this error may result in an {@code
+ * IllegalStateException} only upon some subsequent operation on
+ * this phaser, if ever.
+ *
+ * @return the arrival phase number, or the (negative)
+ * {@linkplain #getPhase() current phase} if terminated
+ * @throws IllegalStateException if not terminated and the number
+ * of unarrived parties would become negative
+ */
+ public int arriveAndAwaitAdvance() {
+ // Specialization of doArrive+awaitAdvance eliminating some reads/paths
+ final Phaser root = this.root;
+ for (;;) {
+ long s = (root == this) ? state : reconcileState();
+ int phase = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ int counts = (int)s;
+ int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
+ if (unarrived <= 0)
+ throw new IllegalStateException(badArrive(s));
+ if (UNSAFE.compareAndSwapLong(this, stateOffset, s,
+ s -= ONE_ARRIVAL)) {
+ if (unarrived > 1)
+ return root.internalAwaitAdvance(phase, null);
+ if (root != this)
+ return parent.arriveAndAwaitAdvance();
+ long n = s & PARTIES_MASK; // base of next state
+ int nextUnarrived = (int)n >>> PARTIES_SHIFT;
+ if (onAdvance(phase, nextUnarrived))
+ n |= TERMINATION_BIT;
+ else if (nextUnarrived == 0)
+ n |= EMPTY;
+ else
+ n |= nextUnarrived;
+ int nextPhase = (phase + 1) & MAX_PHASE;
+ n |= (long)nextPhase << PHASE_SHIFT;
+ if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))
+ return (int)(state >>> PHASE_SHIFT); // terminated
+ releaseWaiters(phase);
+ return nextPhase;
+ }
+ }
+ }
+
+ /**
+ * Awaits the phase of this phaser to advance from the given phase
+ * value, returning immediately if the current phase is not equal
+ * to the given phase value or this phaser is terminated.
+ *
+ * @param phase an arrival phase number, or negative value if
+ * terminated; this argument is normally the value returned by a
+ * previous call to {@code arrive} or {@code arriveAndDeregister}.
+ * @return the next arrival phase number, or the argument if it is
+ * negative, or the (negative) {@linkplain #getPhase() current phase}
+ * if terminated
+ */
+ public int awaitAdvance(int phase) {
+ final Phaser root = this.root;
+ long s = (root == this) ? state : reconcileState();
+ int p = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ if (p == phase)
+ return root.internalAwaitAdvance(phase, null);
+ return p;
+ }
+
+ /**
+ * Awaits the phase of this phaser to advance from the given phase
+ * value, throwing {@code InterruptedException} if interrupted
+ * while waiting, or returning immediately if the current phase is
+ * not equal to the given phase value or this phaser is
+ * terminated.
+ *
+ * @param phase an arrival phase number, or negative value if
+ * terminated; this argument is normally the value returned by a
+ * previous call to {@code arrive} or {@code arriveAndDeregister}.
+ * @return the next arrival phase number, or the argument if it is
+ * negative, or the (negative) {@linkplain #getPhase() current phase}
+ * if terminated
+ * @throws InterruptedException if thread interrupted while waiting
+ */
+ public int awaitAdvanceInterruptibly(int phase)
+ throws InterruptedException {
+ final Phaser root = this.root;
+ long s = (root == this) ? state : reconcileState();
+ int p = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ if (p == phase) {
+ QNode node = new QNode(this, phase, true, false, 0L);
+ p = root.internalAwaitAdvance(phase, node);
+ if (node.wasInterrupted)
+ throw new InterruptedException();
+ }
+ return p;
+ }
+
+ /**
+ * Awaits the phase of this phaser to advance from the given phase
+ * value or the given timeout to elapse, throwing {@code
+ * InterruptedException} if interrupted while waiting, or
+ * returning immediately if the current phase is not equal to the
+ * given phase value or this phaser is terminated.
+ *
+ * @param phase an arrival phase number, or negative value if
+ * terminated; this argument is normally the value returned by a
+ * previous call to {@code arrive} or {@code arriveAndDeregister}.
+ * @param timeout how long to wait before giving up, in units of
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return the next arrival phase number, or the argument if it is
+ * negative, or the (negative) {@linkplain #getPhase() current phase}
+ * if terminated
+ * @throws InterruptedException if thread interrupted while waiting
+ * @throws TimeoutException if timed out while waiting
+ */
+ public int awaitAdvanceInterruptibly(int phase,
+ long timeout, TimeUnit unit)
+ throws InterruptedException, TimeoutException {
+ long nanos = unit.toNanos(timeout);
+ final Phaser root = this.root;
+ long s = (root == this) ? state : reconcileState();
+ int p = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
+ return phase;
+ if (p == phase) {
+ QNode node = new QNode(this, phase, true, true, nanos);
+ p = root.internalAwaitAdvance(phase, node);
+ if (node.wasInterrupted)
+ throw new InterruptedException();
+ else if (p == phase)
+ throw new TimeoutException();
+ }
+ return p;
+ }
+
+ /**
+ * Forces this phaser to enter termination state. Counts of
+ * registered parties are unaffected. If this phaser is a member
+ * of a tiered set of phasers, then all of the phasers in the set
+ * are terminated. If this phaser is already terminated, this
+ * method has no effect. This method may be useful for
+ * coordinating recovery after one or more tasks encounter
+ * unexpected exceptions.
+ */
+ public void forceTermination() {
+ // Only need to change root state
+ final Phaser root = this.root;
+ long s;
+ while ((s = root.state) >= 0) {
+ if (UNSAFE.compareAndSwapLong(root, stateOffset,
+ s, s | TERMINATION_BIT)) {
+ // signal all threads
+ releaseWaiters(0); // Waiters on evenQ
+ releaseWaiters(1); // Waiters on oddQ
+ return;
+ }
+ }
+ }
+
+ /**
+ * Returns the current phase number. The maximum phase number is
+ * {@code Integer.MAX_VALUE}, after which it restarts at
+ * zero. Upon termination, the phase number is negative,
+ * in which case the prevailing phase prior to termination
+ * may be obtained via {@code getPhase() + Integer.MIN_VALUE}.
+ *
+ * @return the phase number, or a negative value if terminated
+ */
+ public final int getPhase() {
+ return (int)(root.state >>> PHASE_SHIFT);
+ }
+
+ /**
+ * Returns the number of parties registered at this phaser.
+ *
+ * @return the number of parties
+ */
+ public int getRegisteredParties() {
+ return partiesOf(state);
+ }
+
+ /**
+ * Returns the number of registered parties that have arrived at
+ * the current phase of this phaser. If this phaser has terminated,
+ * the returned value is meaningless and arbitrary.
+ *
+ * @return the number of arrived parties
+ */
+ public int getArrivedParties() {
+ return arrivedOf(reconcileState());
+ }
+
+ /**
+ * Returns the number of registered parties that have not yet
+ * arrived at the current phase of this phaser. If this phaser has
+ * terminated, the returned value is meaningless and arbitrary.
+ *
+ * @return the number of unarrived parties
+ */
+ public int getUnarrivedParties() {
+ return unarrivedOf(reconcileState());
+ }
+
+ /**
+ * Returns the parent of this phaser, or {@code null} if none.
+ *
+ * @return the parent of this phaser, or {@code null} if none
+ */
+ public Phaser getParent() {
+ return parent;
+ }
+
+ /**
+ * Returns the root ancestor of this phaser, which is the same as
+ * this phaser if it has no parent.
+ *
+ * @return the root ancestor of this phaser
+ */
+ public Phaser getRoot() {
+ return root;
+ }
+
+ /**
+ * Returns {@code true} if this phaser has been terminated.
+ *
+ * @return {@code true} if this phaser has been terminated
+ */
+ public boolean isTerminated() {
+ return root.state < 0L;
+ }
+
+ /**
+ * Overridable method to perform an action upon impending phase
+ * advance, and to control termination. This method is invoked
+ * upon arrival of the party advancing this phaser (when all other
+ * waiting parties are dormant). If this method returns {@code
+ * true}, this phaser will be set to a final termination state
+ * upon advance, and subsequent calls to {@link #isTerminated}
+ * will return true. Any (unchecked) Exception or Error thrown by
+ * an invocation of this method is propagated to the party
+ * attempting to advance this phaser, in which case no advance
+ * occurs.
+ *
+ * <p>The arguments to this method provide the state of the phaser
+ * prevailing for the current transition. The effects of invoking
+ * arrival, registration, and waiting methods on this phaser from
+ * within {@code onAdvance} are unspecified and should not be
+ * relied on.
+ *
+ * <p>If this phaser is a member of a tiered set of phasers, then
+ * {@code onAdvance} is invoked only for its root phaser on each
+ * advance.
+ *
+ * <p>To support the most common use cases, the default
+ * implementation of this method returns {@code true} when the
+ * number of registered parties has become zero as the result of a
+ * party invoking {@code arriveAndDeregister}. You can disable
+ * this behavior, thus enabling continuation upon future
+ * registrations, by overriding this method to always return
+ * {@code false}:
+ *
+ * <pre> {@code
+ * Phaser phaser = new Phaser() {
+ * protected boolean onAdvance(int phase, int parties) { return false; }
+ * }}</pre>
+ *
+ * @param phase the current phase number on entry to this method,
+ * before this phaser is advanced
+ * @param registeredParties the current number of registered parties
+ * @return {@code true} if this phaser should terminate
+ */
+ protected boolean onAdvance(int phase, int registeredParties) {
+ return registeredParties == 0;
+ }
+
+ /**
+ * Returns a string identifying this phaser, as well as its
+ * state. The state, in brackets, includes the String {@code
+ * "phase = "} followed by the phase number, {@code "parties = "}
+ * followed by the number of registered parties, and {@code
+ * "arrived = "} followed by the number of arrived parties.
+ *
+ * @return a string identifying this phaser, as well as its state
+ */
+ public String toString() {
+ return stateToString(reconcileState());
+ }
+
+ /**
+ * Implementation of toString and string-based error messages
+ */
+ private String stateToString(long s) {
+ return super.toString() +
+ "[phase = " + phaseOf(s) +
+ " parties = " + partiesOf(s) +
+ " arrived = " + arrivedOf(s) + "]";
+ }
+
+ // Waiting mechanics
+
+ /**
+ * Removes and signals threads from queue for phase.
+ */
+ private void releaseWaiters(int phase) {
+ QNode q; // first element of queue
+ Thread t; // its thread
+ AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
+ while ((q = head.get()) != null &&
+ q.phase != (int)(root.state >>> PHASE_SHIFT)) {
+ if (head.compareAndSet(q, q.next) &&
+ (t = q.thread) != null) {
+ q.thread = null;
+ LockSupport.unpark(t);
+ }
+ }
+ }
+
+ /**
+ * Variant of releaseWaiters that additionally tries to remove any
+ * nodes no longer waiting for advance due to timeout or
+ * interrupt. Currently, nodes are removed only if they are at
+ * head of queue, which suffices to reduce memory footprint in
+ * most usages.
+ *
+ * @return current phase on exit
+ */
+ private int abortWait(int phase) {
+ AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
+ for (;;) {
+ Thread t;
+ QNode q = head.get();
+ int p = (int)(root.state >>> PHASE_SHIFT);
+ if (q == null || ((t = q.thread) != null && q.phase == p))
+ return p;
+ if (head.compareAndSet(q, q.next) && t != null) {
+ q.thread = null;
+ LockSupport.unpark(t);
+ }
+ }
+ }
+
+ /** The number of CPUs, for spin control */
+ private static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+ /**
+ * The number of times to spin before blocking while waiting for
+ * advance, per arrival while waiting. On multiprocessors, fully
+ * blocking and waking up a large number of threads all at once is
+ * usually a very slow process, so we use rechargeable spins to
+ * avoid it when threads regularly arrive: When a thread in
+ * internalAwaitAdvance notices another arrival before blocking,
+ * and there appear to be enough CPUs available, it spins
+ * SPINS_PER_ARRIVAL more times before blocking. The value trades
+ * off good-citizenship vs big unnecessary slowdowns.
+ */
+ static final int SPINS_PER_ARRIVAL = (NCPU < 2) ? 1 : 1 << 8;
+
+ /**
+ * Possibly blocks and waits for phase to advance unless aborted.
+ * Call only on root phaser.
+ *
+ * @param phase current phase
+ * @param node if non-null, the wait node to track interrupt and timeout;
+ * if null, denotes noninterruptible wait
+ * @return current phase
+ */
+ private int internalAwaitAdvance(int phase, QNode node) {
+ // assert root == this;
+ releaseWaiters(phase-1); // ensure old queue clean
+ boolean queued = false; // true when node is enqueued
+ int lastUnarrived = 0; // to increase spins upon change
+ int spins = SPINS_PER_ARRIVAL;
+ long s;
+ int p;
+ while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) {
+ if (node == null) { // spinning in noninterruptible mode
+ int unarrived = (int)s & UNARRIVED_MASK;
+ if (unarrived != lastUnarrived &&
+ (lastUnarrived = unarrived) < NCPU)
+ spins += SPINS_PER_ARRIVAL;
+ boolean interrupted = Thread.interrupted();
+ if (interrupted || --spins < 0) { // need node to record intr
+ node = new QNode(this, phase, false, false, 0L);
+ node.wasInterrupted = interrupted;
+ }
+ }
+ else if (node.isReleasable()) // done or aborted
+ break;
+ else if (!queued) { // push onto queue
+ AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
+ QNode q = node.next = head.get();
+ if ((q == null || q.phase == phase) &&
+ (int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq
+ queued = head.compareAndSet(q, node);
+ }
+ else {
+ try {
+ ForkJoinPool.managedBlock(node);
+ } catch (InterruptedException ie) {
+ node.wasInterrupted = true;
+ }
+ }
+ }
+
+ if (node != null) {
+ if (node.thread != null)
+ node.thread = null; // avoid need for unpark()
+ if (node.wasInterrupted && !node.interruptible)
+ Thread.currentThread().interrupt();
+ if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase)
+ return abortWait(phase); // possibly clean up on abort
+ }
+ releaseWaiters(phase);
+ return p;
+ }
+
+ /**
+ * Wait nodes for Treiber stack representing wait queue
+ */
+ static final class QNode implements ForkJoinPool.ManagedBlocker {
+ final Phaser phaser;
+ final int phase;
+ final boolean interruptible;
+ final boolean timed;
+ boolean wasInterrupted;
+ long nanos;
+ long lastTime;
+ volatile Thread thread; // nulled to cancel wait
+ QNode next;
+
+ QNode(Phaser phaser, int phase, boolean interruptible,
+ boolean timed, long nanos) {
+ this.phaser = phaser;
+ this.phase = phase;
+ this.interruptible = interruptible;
+ this.nanos = nanos;
+ this.timed = timed;
+ this.lastTime = timed ? System.nanoTime() : 0L;
+ thread = Thread.currentThread();
+ }
+
+ public boolean isReleasable() {
+ if (thread == null)
+ return true;
+ if (phaser.getPhase() != phase) {
+ thread = null;
+ return true;
+ }
+ if (Thread.interrupted())
+ wasInterrupted = true;
+ if (wasInterrupted && interruptible) {
+ thread = null;
+ return true;
+ }
+ if (timed) {
+ if (nanos > 0L) {
+ long now = System.nanoTime();
+ nanos -= now - lastTime;
+ lastTime = now;
+ }
+ if (nanos <= 0L) {
+ thread = null;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean block() {
+ if (isReleasable())
+ return true;
+ else if (!timed)
+ LockSupport.park(this);
+ else if (nanos > 0)
+ LockSupport.parkNanos(this, nanos);
+ return isReleasable();
+ }
+ }
+
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long stateOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = Phaser.class;
+ stateOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("state"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
index cffbe64..26c72eb 100644
--- a/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -81,7 +81,7 @@
* java.util.PriorityQueue operations within a lock, as was done
* in a previous version of this class. To maintain
* interoperability, a plain PriorityQueue is still used during
- * serialization, which maintains compatibility at the espense of
+ * serialization, which maintains compatibility at the expense of
* transiently doubling overhead.
*/
@@ -139,7 +139,7 @@
* to maintain compatibility with previous versions
* of this class. Non-null only during serialization/deserialization.
*/
- private PriorityQueue q;
+ private PriorityQueue<E> q;
/**
* Creates a {@code PriorityBlockingQueue} with the default
@@ -278,14 +278,13 @@
/**
* Mechanics for poll(). Call only while holding lock.
*/
- private E extract() {
- E result;
+ private E dequeue() {
int n = size - 1;
if (n < 0)
- result = null;
+ return null;
else {
Object[] array = queue;
- result = (E) array[0];
+ E result = (E) array[0];
E x = (E) array[n];
array[n] = null;
Comparator<? super E> cmp = comparator;
@@ -294,8 +293,8 @@
else
siftDownUsingComparator(0, x, array, n, cmp);
size = n;
+ return result;
}
- return result;
}
/**
@@ -312,6 +311,7 @@
* @param k the position to fill
* @param x the item to insert
* @param array the heap array
+ * @param n heap size
*/
private static <T> void siftUpComparable(int k, T x, Object[] array) {
Comparable<? super T> key = (Comparable<? super T>) x;
@@ -476,7 +476,7 @@
* @param timeout This parameter is ignored as the method never blocks
* @param unit This parameter is ignored as the method never blocks
* @return {@code true} (as specified by
- * {@link BlockingQueue#offer BlockingQueue.offer})
+ * {@link BlockingQueue#offer(Object,long,TimeUnit) BlockingQueue.offer})
* @throws ClassCastException if the specified element cannot be compared
* with elements currently in the priority queue according to the
* priority queue's ordering
@@ -489,13 +489,11 @@
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
- E result;
try {
- result = extract();
+ return dequeue();
} finally {
lock.unlock();
}
- return result;
}
public E take() throws InterruptedException {
@@ -503,7 +501,7 @@
lock.lockInterruptibly();
E result;
try {
- while ( (result = extract()) == null)
+ while ( (result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
@@ -517,7 +515,7 @@
lock.lockInterruptibly();
E result;
try {
- while ( (result = extract()) == null && nanos > 0)
+ while ( (result = dequeue()) == null && nanos > 0)
nanos = notEmpty.awaitNanos(nanos);
} finally {
lock.unlock();
@@ -528,13 +526,11 @@
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
- E result;
try {
- result = size > 0 ? (E) queue[0] : null;
+ return (size == 0) ? null : (E) queue[0];
} finally {
lock.unlock();
}
- return result;
}
/**
@@ -618,32 +614,28 @@
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
- boolean removed = false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = indexOf(o);
- if (i != -1) {
- removeAt(i);
- removed = true;
- }
+ if (i == -1)
+ return false;
+ removeAt(i);
+ return true;
} finally {
lock.unlock();
}
- return removed;
}
-
/**
* Identity-based version for use in Itr.remove
*/
- private void removeEQ(Object o) {
+ void removeEQ(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] array = queue;
- int n = size;
- for (int i = 0; i < n; i++) {
+ for (int i = 0, n = size; i < n; i++) {
if (o == array[i]) {
removeAt(i);
break;
@@ -663,15 +655,13 @@
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
- int index;
final ReentrantLock lock = this.lock;
lock.lock();
try {
- index = indexOf(o);
+ return indexOf(o) != -1;
} finally {
lock.unlock();
}
- return index != -1;
}
/**
@@ -697,7 +687,6 @@
}
}
-
public String toString() {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -708,7 +697,7 @@
StringBuilder sb = new StringBuilder();
sb.append('[');
for (int i = 0; i < n; ++i) {
- E e = (E)queue[i];
+ Object e = queue[i];
sb.append(e == this ? "(this Collection)" : e);
if (i != n - 1)
sb.append(',').append(' ');
@@ -726,23 +715,7 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
- if (c == null)
- throw new NullPointerException();
- if (c == this)
- throw new IllegalArgumentException();
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int n = 0;
- E e;
- while ( (e = extract()) != null) {
- c.add(e);
- ++n;
- }
- return n;
- } finally {
- lock.unlock();
- }
+ return drainTo(c, Integer.MAX_VALUE);
}
/**
@@ -761,11 +734,10 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- int n = 0;
- E e;
- while (n < maxElements && (e = extract()) != null) {
- c.add(e);
- ++n;
+ int n = Math.min(size, maxElements);
+ for (int i = 0; i < n; i++) {
+ c.add((E) queue[0]); // In this order, in case add() throws.
+ dequeue();
}
return n;
} finally {
@@ -813,8 +785,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -867,7 +838,7 @@
*/
final class Itr implements Iterator<E> {
final Object[] array; // Array of all elements
- int cursor; // index of next element to return;
+ int cursor; // index of next element to return
int lastRet; // index of last element, or -1 if no such
Itr(Object[] array) {
@@ -904,8 +875,8 @@
throws java.io.IOException {
lock.lock();
try {
- int n = size; // avoid zero capacity argument
- q = new PriorityQueue<E>(n == 0 ? 1 : n, comparator);
+ // avoid zero capacity argument
+ q = new PriorityQueue<E>(Math.max(size, 1), comparator);
q.addAll(this);
s.defaultWriteObject();
} finally {
@@ -933,21 +904,16 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long allocationSpinLockOffset =
- objectFieldOffset(UNSAFE, "allocationSpinLock",
- PriorityBlockingQueue.class);
-
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long allocationSpinLockOffset;
+ static {
try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = PriorityBlockingQueue.class;
+ allocationSpinLockOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("allocationSpinLock"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
-
}
diff --git a/luni/src/main/java/java/util/concurrent/RecursiveAction.java b/luni/src/main/java/java/util/concurrent/RecursiveAction.java
new file mode 100644
index 0000000..48066c9
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/RecursiveAction.java
@@ -0,0 +1,165 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+/**
+ * A recursive resultless {@link ForkJoinTask}. This class
+ * establishes conventions to parameterize resultless actions as
+ * {@code Void} {@code ForkJoinTask}s. Because {@code null} is the
+ * only valid value of type {@code Void}, methods such as {@code join}
+ * always return {@code null} upon completion.
+ *
+ * <p><b>Sample Usages.</b> Here is a simple but complete ForkJoin
+ * sort that sorts a given {@code long[]} array:
+ *
+ * <pre> {@code
+ * static class SortTask extends RecursiveAction {
+ * final long[] array; final int lo, hi;
+ * SortTask(long[] array, int lo, int hi) {
+ * this.array = array; this.lo = lo; this.hi = hi;
+ * }
+ * SortTask(long[] array) { this(array, 0, array.length); }
+ * protected void compute() {
+ * if (hi - lo < THRESHOLD)
+ * sortSequentially(lo, hi);
+ * else {
+ * int mid = (lo + hi) >>> 1;
+ * invokeAll(new SortTask(array, lo, mid),
+ * new SortTask(array, mid, hi));
+ * merge(lo, mid, hi);
+ * }
+ * }
+ * // implementation details follow:
+ * final static int THRESHOLD = 1000;
+ * void sortSequentially(int lo, int hi) {
+ * Arrays.sort(array, lo, hi);
+ * }
+ * void merge(int lo, int mid, int hi) {
+ * long[] buf = Arrays.copyOfRange(array, lo, mid);
+ * for (int i = 0, j = lo, k = mid; i < buf.length; j++)
+ * array[j] = (k == hi || buf[i] < array[k]) ?
+ * buf[i++] : array[k++];
+ * }
+ * }}</pre>
+ *
+ * You could then sort {@code anArray} by creating {@code new
+ * SortTask(anArray)} and invoking it in a ForkJoinPool. As a more
+ * concrete simple example, the following task increments each element
+ * of an array:
+ * <pre> {@code
+ * class IncrementTask extends RecursiveAction {
+ * final long[] array; final int lo, hi;
+ * IncrementTask(long[] array, int lo, int hi) {
+ * this.array = array; this.lo = lo; this.hi = hi;
+ * }
+ * protected void compute() {
+ * if (hi - lo < THRESHOLD) {
+ * for (int i = lo; i < hi; ++i)
+ * array[i]++;
+ * }
+ * else {
+ * int mid = (lo + hi) >>> 1;
+ * invokeAll(new IncrementTask(array, lo, mid),
+ * new IncrementTask(array, mid, hi));
+ * }
+ * }
+ * }}</pre>
+ *
+ * <p>The following example illustrates some refinements and idioms
+ * that may lead to better performance: RecursiveActions need not be
+ * fully recursive, so long as they maintain the basic
+ * divide-and-conquer approach. Here is a class that sums the squares
+ * of each element of a double array, by subdividing out only the
+ * right-hand-sides of repeated divisions by two, and keeping track of
+ * them with a chain of {@code next} references. It uses a dynamic
+ * threshold based on method {@code getSurplusQueuedTaskCount}, but
+ * counterbalances potential excess partitioning by directly
+ * performing leaf actions on unstolen tasks rather than further
+ * subdividing.
+ *
+ * <pre> {@code
+ * double sumOfSquares(ForkJoinPool pool, double[] array) {
+ * int n = array.length;
+ * Applyer a = new Applyer(array, 0, n, null);
+ * pool.invoke(a);
+ * return a.result;
+ * }
+ *
+ * class Applyer extends RecursiveAction {
+ * final double[] array;
+ * final int lo, hi;
+ * double result;
+ * Applyer next; // keeps track of right-hand-side tasks
+ * Applyer(double[] array, int lo, int hi, Applyer next) {
+ * this.array = array; this.lo = lo; this.hi = hi;
+ * this.next = next;
+ * }
+ *
+ * double atLeaf(int l, int h) {
+ * double sum = 0;
+ * for (int i = l; i < h; ++i) // perform leftmost base step
+ * sum += array[i] * array[i];
+ * return sum;
+ * }
+ *
+ * protected void compute() {
+ * int l = lo;
+ * int h = hi;
+ * Applyer right = null;
+ * while (h - l > 1 && getSurplusQueuedTaskCount() <= 3) {
+ * int mid = (l + h) >>> 1;
+ * right = new Applyer(array, mid, h, right);
+ * right.fork();
+ * h = mid;
+ * }
+ * double sum = atLeaf(l, h);
+ * while (right != null) {
+ * if (right.tryUnfork()) // directly calculate if not stolen
+ * sum += right.atLeaf(right.lo, right.hi);
+ * else {
+ * right.join();
+ * sum += right.result;
+ * }
+ * right = right.next;
+ * }
+ * result = sum;
+ * }
+ * }}</pre>
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public abstract class RecursiveAction extends ForkJoinTask<Void> {
+ private static final long serialVersionUID = 5232453952276485070L;
+
+ /**
+ * The main computation performed by this task.
+ */
+ protected abstract void compute();
+
+ /**
+ * Always returns {@code null}.
+ *
+ * @return {@code null} always
+ */
+ public final Void getRawResult() { return null; }
+
+ /**
+ * Requires null completion value.
+ */
+ protected final void setRawResult(Void mustBeNull) { }
+
+ /**
+ * Implements execution conventions for RecursiveActions.
+ */
+ protected final boolean exec() {
+ compute();
+ return true;
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/RecursiveTask.java b/luni/src/main/java/java/util/concurrent/RecursiveTask.java
new file mode 100644
index 0000000..5e17280
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/RecursiveTask.java
@@ -0,0 +1,69 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+/**
+ * A recursive result-bearing {@link ForkJoinTask}.
+ *
+ * <p>For a classic example, here is a task computing Fibonacci numbers:
+ *
+ * <pre> {@code
+ * class Fibonacci extends RecursiveTask<Integer> {
+ * final int n;
+ * Fibonacci(int n) { this.n = n; }
+ * Integer compute() {
+ * if (n <= 1)
+ * return n;
+ * Fibonacci f1 = new Fibonacci(n - 1);
+ * f1.fork();
+ * Fibonacci f2 = new Fibonacci(n - 2);
+ * return f2.compute() + f1.join();
+ * }
+ * }}</pre>
+ *
+ * However, besides being a dumb way to compute Fibonacci functions
+ * (there is a simple fast linear algorithm that you'd use in
+ * practice), this is likely to perform poorly because the smallest
+ * subtasks are too small to be worthwhile splitting up. Instead, as
+ * is the case for nearly all fork/join applications, you'd pick some
+ * minimum granularity size (for example 10 here) for which you always
+ * sequentially solve rather than subdividing.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
+ private static final long serialVersionUID = 5232453952276485270L;
+
+ /**
+ * The result of the computation.
+ */
+ V result;
+
+ /**
+ * The main computation performed by this task.
+ */
+ protected abstract V compute();
+
+ public final V getRawResult() {
+ return result;
+ }
+
+ protected final void setRawResult(V value) {
+ result = value;
+ }
+
+ /**
+ * Implements execution conventions for RecursiveTask.
+ */
+ protected final boolean exec() {
+ result = compute();
+ return true;
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java b/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java
index 30b043d..f0005d1 100644
--- a/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java
+++ b/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -49,8 +49,8 @@
/**
* Constructs a <tt>RejectedExecutionException</tt> with the
- * specified cause. The detail message is set to: <pre> (cause ==
- * null ? null : cause.toString())</pre> (which typically contains
+ * specified cause. The detail message is set to {@code (cause ==
+ * null ? null : cause.toString())} (which typically contains
* the class and detail message of <tt>cause</tt>).
*
* @param cause the cause (which is saved for later retrieval by the
diff --git a/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java b/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
index 417a27c..8c000ea 100644
--- a/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
+++ b/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/RunnableFuture.java b/luni/src/main/java/java/util/concurrent/RunnableFuture.java
index d74211d..2d6d52c 100644
--- a/luni/src/main/java/java/util/concurrent/RunnableFuture.java
+++ b/luni/src/main/java/java/util/concurrent/RunnableFuture.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
index 0e8cc32..fbb995c 100644
--- a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
+++ b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
index 6cb4e27..71e57ed 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
@@ -1,12 +1,10 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.atomic.*;
-import java.util.*;
/**
* An {@link ExecutorService} that can schedule commands to run after a given
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledFuture.java b/luni/src/main/java/java/util/concurrent/ScheduledFuture.java
index 239d681..3745cb0 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledFuture.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledFuture.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
index c2eaedf..e41f0c3 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
@@ -1,16 +1,19 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
-import java.util.concurrent.atomic.*;
-import java.util.concurrent.locks.*;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
// BEGIN android-note
-// Omit class-level docs on setRemoveOnCancelPolicy()
+// omit class-level docs on setRemoveOnCancelPolicy()
+// removed security manager docs
// END android-note
/**
@@ -138,7 +141,7 @@
* Sequence number to break scheduling ties, and in turn to
* guarantee FIFO order among tied entries.
*/
- private static final AtomicLong sequencer = new AtomicLong(0);
+ private static final AtomicLong sequencer = new AtomicLong();
/**
* Returns current nanosecond time.
@@ -203,11 +206,11 @@
}
public long getDelay(TimeUnit unit) {
- return unit.convert(time - now(), TimeUnit.NANOSECONDS);
+ return unit.convert(time - now(), NANOSECONDS);
}
public int compareTo(Delayed other) {
- if (other == this) // compare zero ONLY if same object
+ if (other == this) // compare zero if same object
return 0;
if (other instanceof ScheduledFutureTask) {
ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
@@ -221,9 +224,9 @@
else
return 1;
}
- long d = (getDelay(TimeUnit.NANOSECONDS) -
- other.getDelay(TimeUnit.NANOSECONDS));
- return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
+ long diff = (getDelay(NANOSECONDS) -
+ other.getDelay(NANOSECONDS));
+ return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
/**
@@ -302,7 +305,7 @@
remove(task))
task.cancel(false);
else
- prestartCoreThread();
+ ensurePrestart();
}
}
@@ -318,7 +321,7 @@
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
- prestartCoreThread();
+ ensurePrestart();
}
}
@@ -396,7 +399,7 @@
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
- super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
@@ -413,7 +416,7 @@
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
- super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
@@ -430,7 +433,7 @@
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler) {
- super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), handler);
}
@@ -451,7 +454,7 @@
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
- super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
@@ -480,7 +483,7 @@
private long overflowFree(long delay) {
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
- long headDelay = head.getDelay(TimeUnit.NANOSECONDS);
+ long headDelay = head.getDelay(NANOSECONDS);
if (headDelay < 0 && (delay - headDelay < 0))
delay = Long.MAX_VALUE + headDelay;
}
@@ -588,7 +591,7 @@
* @throws NullPointerException {@inheritDoc}
*/
public void execute(Runnable command) {
- schedule(command, 0, TimeUnit.NANOSECONDS);
+ schedule(command, 0, NANOSECONDS);
}
// Override AbstractExecutorService methods
@@ -598,7 +601,7 @@
* @throws NullPointerException {@inheritDoc}
*/
public Future<?> submit(Runnable task) {
- return schedule(task, 0, TimeUnit.NANOSECONDS);
+ return schedule(task, 0, NANOSECONDS);
}
/**
@@ -606,8 +609,7 @@
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Runnable task, T result) {
- return schedule(Executors.callable(task, result),
- 0, TimeUnit.NANOSECONDS);
+ return schedule(Executors.callable(task, result), 0, NANOSECONDS);
}
/**
@@ -615,7 +617,7 @@
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
- return schedule(task, 0, TimeUnit.NANOSECONDS);
+ return schedule(task, 0, NANOSECONDS);
}
/**
@@ -690,8 +692,9 @@
* @param value if {@code true}, remove on cancellation, else don't
* @see #getRemoveOnCancelPolicy
* @since 1.7
+ * @hide
*/
- /*public*/ void setRemoveOnCancelPolicy(boolean value) { // android-changed
+ public void setRemoveOnCancelPolicy(boolean value) {
removeOnCancel = value;
}
@@ -704,8 +707,9 @@
* from the queue
* @see #setRemoveOnCancelPolicy
* @since 1.7
+ * @hide
*/
- /*public*/ boolean getRemoveOnCancelPolicy() { // android-changed
+ public boolean getRemoveOnCancelPolicy() {
return removeOnCancel;
}
@@ -724,8 +728,6 @@
* ContinueExistingPeriodicTasksAfterShutdownPolicy} has been set
* {@code true}, future executions of existing periodic tasks will
* be cancelled.
- *
- * @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
super.shutdown();
@@ -750,7 +752,6 @@
* including those tasks submitted using {@code execute},
* which are for scheduling purposes used as the basis of a
* zero-delay {@code ScheduledFuture}.
- * @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
return super.shutdownNow();
@@ -803,8 +804,8 @@
*/
private static final int INITIAL_CAPACITY = 16;
- private RunnableScheduledFuture[] queue =
- new RunnableScheduledFuture[INITIAL_CAPACITY];
+ private RunnableScheduledFuture<?>[] queue =
+ new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
private int size = 0;
@@ -835,7 +836,7 @@
/**
* Set f's heapIndex if it is a ScheduledFutureTask.
*/
- private void setIndex(RunnableScheduledFuture f, int idx) {
+ private void setIndex(RunnableScheduledFuture<?> f, int idx) {
if (f instanceof ScheduledFutureTask)
((ScheduledFutureTask)f).heapIndex = idx;
}
@@ -844,10 +845,10 @@
* Sift element added at bottom up to its heap-ordered spot.
* Call only when holding lock.
*/
- private void siftUp(int k, RunnableScheduledFuture key) {
+ private void siftUp(int k, RunnableScheduledFuture<?> key) {
while (k > 0) {
int parent = (k - 1) >>> 1;
- RunnableScheduledFuture e = queue[parent];
+ RunnableScheduledFuture<?> e = queue[parent];
if (key.compareTo(e) >= 0)
break;
queue[k] = e;
@@ -862,11 +863,11 @@
* Sift element added at top down to its heap-ordered spot.
* Call only when holding lock.
*/
- private void siftDown(int k, RunnableScheduledFuture key) {
+ private void siftDown(int k, RunnableScheduledFuture<?> key) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
- RunnableScheduledFuture c = queue[child];
+ RunnableScheduledFuture<?> c = queue[child];
int right = child + 1;
if (right < size && c.compareTo(queue[right]) > 0)
c = queue[child = right];
@@ -931,7 +932,7 @@
setIndex(queue[i], -1);
int s = --size;
- RunnableScheduledFuture replacement = queue[s];
+ RunnableScheduledFuture<?> replacement = queue[s];
queue[s] = null;
if (s != i) {
siftDown(i, replacement);
@@ -962,7 +963,7 @@
return Integer.MAX_VALUE;
}
- public RunnableScheduledFuture peek() {
+ public RunnableScheduledFuture<?> peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
@@ -975,7 +976,7 @@
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();
- RunnableScheduledFuture e = (RunnableScheduledFuture)x;
+ RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
final ReentrantLock lock = this.lock;
lock.lock();
try {
@@ -1017,9 +1018,9 @@
* holding lock.
* @param f the task to remove and return
*/
- private RunnableScheduledFuture finishPoll(RunnableScheduledFuture f) {
+ private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) {
int s = --size;
- RunnableScheduledFuture x = queue[s];
+ RunnableScheduledFuture<?> x = queue[s];
queue[s] = null;
if (s != 0)
siftDown(0, x);
@@ -1027,12 +1028,12 @@
return f;
}
- public RunnableScheduledFuture poll() {
+ public RunnableScheduledFuture<?> poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- RunnableScheduledFuture first = queue[0];
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
+ RunnableScheduledFuture<?> first = queue[0];
+ if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return finishPoll(first);
@@ -1041,16 +1042,16 @@
}
}
- public RunnableScheduledFuture take() throws InterruptedException {
+ public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
- RunnableScheduledFuture first = queue[0];
+ RunnableScheduledFuture<?> first = queue[0];
if (first == null)
available.await();
else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
else if (leader != null)
@@ -1074,21 +1075,21 @@
}
}
- public RunnableScheduledFuture poll(long timeout, TimeUnit unit)
+ public RunnableScheduledFuture<?> poll(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
- RunnableScheduledFuture first = queue[0];
+ RunnableScheduledFuture<?> first = queue[0];
if (first == null) {
if (nanos <= 0)
return null;
else
nanos = available.awaitNanos(nanos);
} else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
if (nanos <= 0)
@@ -1120,7 +1121,7 @@
lock.lock();
try {
for (int i = 0; i < size; i++) {
- RunnableScheduledFuture t = queue[i];
+ RunnableScheduledFuture<?> t = queue[i];
if (t != null) {
queue[i] = null;
setIndex(t, -1);
@@ -1133,14 +1134,14 @@
}
/**
- * Return and remove first element only if it is expired.
+ * Return first element only if it is expired.
* Used only by drainTo. Call only when holding lock.
*/
- private RunnableScheduledFuture pollExpired() {
- RunnableScheduledFuture first = queue[0];
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- return null;
- return finishPoll(first);
+ private RunnableScheduledFuture<?> peekExpired() {
+ // assert lock.isHeldByCurrentThread();
+ RunnableScheduledFuture<?> first = queue[0];
+ return (first == null || first.getDelay(NANOSECONDS) > 0) ?
+ null : first;
}
public int drainTo(Collection<? super Runnable> c) {
@@ -1151,10 +1152,11 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- RunnableScheduledFuture first;
+ RunnableScheduledFuture<?> first;
int n = 0;
- while ((first = pollExpired()) != null) {
- c.add(first);
+ while ((first = peekExpired()) != null) {
+ c.add(first); // In this order, in case add() throws.
+ finishPoll(first);
++n;
}
return n;
@@ -1173,10 +1175,11 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- RunnableScheduledFuture first;
+ RunnableScheduledFuture<?> first;
int n = 0;
- while (n < maxElements && (first = pollExpired()) != null) {
- c.add(first);
+ while (n < maxElements && (first = peekExpired()) != null) {
+ c.add(first); // In this order, in case add() throws.
+ finishPoll(first);
++n;
}
return n;
diff --git a/luni/src/main/java/java/util/concurrent/Semaphore.java b/luni/src/main/java/java/util/concurrent/Semaphore.java
index 62dea1a..bf2524c 100644
--- a/luni/src/main/java/java/util/concurrent/Semaphore.java
+++ b/luni/src/main/java/java/util/concurrent/Semaphore.java
@@ -1,13 +1,12 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.*;
import java.util.concurrent.locks.*;
-import java.util.concurrent.atomic.*;
/**
* A counting semaphore. Conceptually, a semaphore maintains a set of
@@ -20,7 +19,7 @@
* <p>Semaphores are often used to restrict the number of threads than can
* access some (physical or logical) resource. For example, here is
* a class that uses a semaphore to control access to a pool of items:
- * <pre>
+ * <pre> {@code
* class Pool {
* private static final int MAX_AVAILABLE = 100;
* private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
@@ -62,9 +61,7 @@
* }
* return false;
* }
- *
- * }
- * </pre>
+ * }}</pre>
*
* <p>Before obtaining an item each thread must acquire a permit from
* the semaphore, guaranteeing that an item is available for use. When
diff --git a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
index 51d40c0..b05ae0a 100644
--- a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
+++ b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
@@ -2,13 +2,12 @@
* Written by Doug Lea, Bill Scherer, and Michael Scott with
* assistance from members of JCP JSR-166 Expert Group and released to
* the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
import java.util.concurrent.locks.*;
import java.util.*;
-import libcore.util.EmptyArray;
// BEGIN android-note
// removed link to collections framework docs
@@ -250,12 +249,22 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", SNode.class);
- private static final long matchOffset =
- objectFieldOffset(UNSAFE, "match", SNode.class);
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long matchOffset;
+ private static final long nextOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = SNode.class;
+ matchOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("match"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/** The head (top) of the stack */
@@ -393,7 +402,6 @@
*/
long lastTime = timed ? System.nanoTime() : 0;
Thread w = Thread.currentThread();
- SNode h = head;
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
@@ -469,10 +477,18 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", TransferStack.class);
-
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = TransferStack.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/** Dual Queue */
@@ -529,11 +545,22 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long nextOffset =
- objectFieldOffset(UNSAFE, "next", QNode.class);
- private static final long itemOffset =
- objectFieldOffset(UNSAFE, "item", QNode.class);
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long itemOffset;
+ private static final long nextOffset;
+
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = QNode.class;
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/** Head of queue */
@@ -762,15 +789,24 @@
}
}
- // unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long headOffset =
- objectFieldOffset(UNSAFE, "head", TransferQueue.class);
- private static final long tailOffset =
- objectFieldOffset(UNSAFE, "tail", TransferQueue.class);
- private static final long cleanMeOffset =
- objectFieldOffset(UNSAFE, "cleanMe", TransferQueue.class);
-
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long headOffset;
+ private static final long tailOffset;
+ private static final long cleanMeOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = TransferQueue.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ cleanMeOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("cleanMe"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
}
/**
@@ -998,8 +1034,19 @@
*
* @return an empty iterator
*/
+ @SuppressWarnings("unchecked")
public Iterator<E> iterator() {
- return Collections.<E>emptySet().iterator(); // android-changed
+ return (Iterator<E>) EmptyIterator.EMPTY_ITERATOR;
+ }
+
+ // Replicated from a previous version of Collections
+ private static class EmptyIterator<E> implements Iterator<E> {
+ static final EmptyIterator<Object> EMPTY_ITERATOR
+ = new EmptyIterator<Object>();
+
+ public boolean hasNext() { return false; }
+ public E next() { throw new NoSuchElementException(); }
+ public void remove() { throw new IllegalStateException(); }
}
/**
@@ -1007,7 +1054,7 @@
* @return a zero-length array
*/
public Object[] toArray() {
- return EmptyArray.OBJECT; // android-changed
+ return new Object[0];
}
/**
@@ -1036,8 +1083,7 @@
if (c == this)
throw new IllegalArgumentException();
int n = 0;
- E e;
- while ( (e = poll()) != null) {
+ for (E e; (e = poll()) != null;) {
c.add(e);
++n;
}
@@ -1056,8 +1102,7 @@
if (c == this)
throw new IllegalArgumentException();
int n = 0;
- E e;
- while (n < maxElements && (e = poll()) != null) {
+ for (E e; n < maxElements && (e = poll()) != null;) {
c.add(e);
++n;
}
@@ -1084,7 +1129,7 @@
private WaitQueue waitingConsumers;
/**
- * Save the state to a stream (that is, serialize it).
+ * Saves the state to a stream (that is, serializes it).
*
* @param s the stream
*/
diff --git a/luni/src/main/java/java/util/concurrent/ThreadFactory.java b/luni/src/main/java/java/util/concurrent/ThreadFactory.java
index 2f0fb1a..d1a4eb6 100644
--- a/luni/src/main/java/java/util/concurrent/ThreadFactory.java
+++ b/luni/src/main/java/java/util/concurrent/ThreadFactory.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -13,13 +13,12 @@
*
* <p>
* The simplest implementation of this interface is just:
- * <pre>
+ * <pre> {@code
* class SimpleThreadFactory implements ThreadFactory {
* public Thread newThread(Runnable r) {
* return new Thread(r);
* }
- * }
- * </pre>
+ * }}</pre>
*
* The {@link Executors#defaultThreadFactory} method provides a more
* useful simple implementation, that sets the created thread context
diff --git a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
new file mode 100644
index 0000000..a559321
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
@@ -0,0 +1,198 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+import java.util.Random;
+
+/**
+ * A random number generator isolated to the current thread. Like the
+ * global {@link java.util.Random} generator used by the {@link
+ * java.lang.Math} class, a {@code ThreadLocalRandom} is initialized
+ * with an internally generated seed that may not otherwise be
+ * modified. When applicable, use of {@code ThreadLocalRandom} rather
+ * than shared {@code Random} objects in concurrent programs will
+ * typically encounter much less overhead and contention. Use of
+ * {@code ThreadLocalRandom} is particularly appropriate when multiple
+ * tasks (for example, each a {@link ForkJoinTask}) use random numbers
+ * in parallel in thread pools.
+ *
+ * <p>Usages of this class should typically be of the form:
+ * {@code ThreadLocalRandom.current().nextX(...)} (where
+ * {@code X} is {@code Int}, {@code Long}, etc).
+ * When all usages are of this form, it is never possible to
+ * accidently share a {@code ThreadLocalRandom} across multiple threads.
+ *
+ * <p>This class also provides additional commonly used bounded random
+ * generation methods.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class ThreadLocalRandom extends Random {
+ // same constants as Random, but must be redeclared because private
+ private static final long multiplier = 0x5DEECE66DL;
+ private static final long addend = 0xBL;
+ private static final long mask = (1L << 48) - 1;
+
+ /**
+ * The random seed. We can't use super.seed.
+ */
+ private long rnd;
+
+ /**
+ * Initialization flag to permit calls to setSeed to succeed only
+ * while executing the Random constructor. We can't allow others
+ * since it would cause setting seed in one part of a program to
+ * unintentionally impact other usages by the thread.
+ */
+ boolean initialized;
+
+ // Padding to help avoid memory contention among seed updates in
+ // different TLRs in the common case that they are located near
+ // each other.
+ private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
+
+ /**
+ * The actual ThreadLocal
+ */
+ private static final ThreadLocal<ThreadLocalRandom> localRandom =
+ new ThreadLocal<ThreadLocalRandom>() {
+ protected ThreadLocalRandom initialValue() {
+ return new ThreadLocalRandom();
+ }
+ };
+
+
+ /**
+ * Constructor called only by localRandom.initialValue.
+ */
+ ThreadLocalRandom() {
+ super();
+ initialized = true;
+ }
+
+ /**
+ * Returns the current thread's {@code ThreadLocalRandom}.
+ *
+ * @return the current thread's {@code ThreadLocalRandom}
+ */
+ public static ThreadLocalRandom current() {
+ return localRandom.get();
+ }
+
+ /**
+ * Throws {@code UnsupportedOperationException}. Setting seeds in
+ * this generator is not supported.
+ *
+ * @throws UnsupportedOperationException always
+ */
+ public void setSeed(long seed) {
+ if (initialized)
+ throw new UnsupportedOperationException();
+ rnd = (seed ^ multiplier) & mask;
+ }
+
+ protected int next(int bits) {
+ rnd = (rnd * multiplier + addend) & mask;
+ return (int) (rnd >>> (48-bits));
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed value between the
+ * given least value (inclusive) and bound (exclusive).
+ *
+ * @param least the least value returned
+ * @param bound the upper bound (exclusive)
+ * @throws IllegalArgumentException if least greater than or equal
+ * to bound
+ * @return the next value
+ */
+ public int nextInt(int least, int bound) {
+ if (least >= bound)
+ throw new IllegalArgumentException();
+ return nextInt(bound - least) + least;
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed value
+ * between 0 (inclusive) and the specified value (exclusive).
+ *
+ * @param n the bound on the random number to be returned. Must be
+ * positive.
+ * @return the next value
+ * @throws IllegalArgumentException if n is not positive
+ */
+ public long nextLong(long n) {
+ if (n <= 0)
+ throw new IllegalArgumentException("n must be positive");
+ // Divide n by two until small enough for nextInt. On each
+ // iteration (at most 31 of them but usually much less),
+ // randomly choose both whether to include high bit in result
+ // (offset) and whether to continue with the lower vs upper
+ // half (which makes a difference only if odd).
+ long offset = 0;
+ while (n >= Integer.MAX_VALUE) {
+ int bits = next(2);
+ long half = n >>> 1;
+ long nextn = ((bits & 2) == 0) ? half : n - half;
+ if ((bits & 1) == 0)
+ offset += n - nextn;
+ n = nextn;
+ }
+ return offset + nextInt((int) n);
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed value between the
+ * given least value (inclusive) and bound (exclusive).
+ *
+ * @param least the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return the next value
+ * @throws IllegalArgumentException if least greater than or equal
+ * to bound
+ */
+ public long nextLong(long least, long bound) {
+ if (least >= bound)
+ throw new IllegalArgumentException();
+ return nextLong(bound - least) + least;
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed {@code double} value
+ * between 0 (inclusive) and the specified value (exclusive).
+ *
+ * @param n the bound on the random number to be returned. Must be
+ * positive.
+ * @return the next value
+ * @throws IllegalArgumentException if n is not positive
+ */
+ public double nextDouble(double n) {
+ if (n <= 0)
+ throw new IllegalArgumentException("n must be positive");
+ return nextDouble() * n;
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed value between the
+ * given least value (inclusive) and bound (exclusive).
+ *
+ * @param least the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return the next value
+ * @throws IllegalArgumentException if least greater than or equal
+ * to bound
+ */
+ public double nextDouble(double least, double bound) {
+ if (least >= bound)
+ throw new IllegalArgumentException();
+ return nextDouble() * (bound - least) + least;
+ }
+
+ private static final long serialVersionUID = -5851777807851030925L;
+}
diff --git a/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
index 6622af8..331e225 100644
--- a/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
+++ b/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -9,6 +9,10 @@
import java.util.concurrent.atomic.*;
import java.util.*;
+// BEGIN android-note
+// removed security manager docs
+// END android-note
+
/**
* An {@link ExecutorService} that executes each submitted task using
* one of possibly several pooled threads, normally configured
@@ -1311,8 +1315,6 @@
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
- *
- * @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
@@ -1342,8 +1344,6 @@
* processing actively executing tasks. This implementation
* cancels tasks via {@link Thread#interrupt}, so any task that
* fails to respond to interrupts may never terminate.
- *
- * @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
@@ -1512,6 +1512,18 @@
}
/**
+ * Same as prestartCoreThread except arranges that at least one
+ * thread is started even if corePoolSize is 0.
+ */
+ void ensurePrestart() {
+ int wc = workerCountOf(ctl.get());
+ if (wc < corePoolSize)
+ addWorker(null, true);
+ else if (wc == 0)
+ addWorker(null, false);
+ }
+
+ /**
* Starts all core threads, causing them to idly wait for work. This
* overrides the default policy of starting core threads only when
* new tasks are executed.
diff --git a/luni/src/main/java/java/util/concurrent/TimeUnit.java b/luni/src/main/java/java/util/concurrent/TimeUnit.java
index b2e3060..50f6ce0 100644
--- a/luni/src/main/java/java/util/concurrent/TimeUnit.java
+++ b/luni/src/main/java/java/util/concurrent/TimeUnit.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
@@ -23,14 +23,14 @@
* the following code will timeout in 50 milliseconds if the {@link
* java.util.concurrent.locks.Lock lock} is not available:
*
- * <pre> Lock lock = ...;
- * if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...
- * </pre>
+ * <pre> {@code
+ * Lock lock = ...;
+ * if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...}</pre>
+ *
* while this code will timeout in 50 seconds:
- * <pre>
- * Lock lock = ...;
- * if (lock.tryLock(50L, TimeUnit.SECONDS)) ...
- * </pre>
+ * <pre> {@code
+ * Lock lock = ...;
+ * if (lock.tryLock(50L, TimeUnit.SECONDS)) ...}</pre>
*
* Note however, that there is no guarantee that a particular timeout
* implementation will be able to notice the passage of time at the
diff --git a/luni/src/main/java/java/util/concurrent/TimeoutException.java b/luni/src/main/java/java/util/concurrent/TimeoutException.java
index 8b84f28..83934f0 100644
--- a/luni/src/main/java/java/util/concurrent/TimeoutException.java
+++ b/luni/src/main/java/java/util/concurrent/TimeoutException.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent;
diff --git a/luni/src/main/java/java/util/concurrent/TransferQueue.java b/luni/src/main/java/java/util/concurrent/TransferQueue.java
new file mode 100644
index 0000000..9cd5773
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/TransferQueue.java
@@ -0,0 +1,133 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
+/**
+ * A {@link BlockingQueue} in which producers may wait for consumers
+ * to receive elements. A {@code TransferQueue} may be useful for
+ * example in message passing applications in which producers
+ * sometimes (using method {@link #transfer}) await receipt of
+ * elements by consumers invoking {@code take} or {@code poll}, while
+ * at other times enqueue elements (via method {@code put}) without
+ * waiting for receipt.
+ * {@linkplain #tryTransfer(Object) Non-blocking} and
+ * {@linkplain #tryTransfer(Object,long,TimeUnit) time-out} versions of
+ * {@code tryTransfer} are also available.
+ * A {@code TransferQueue} may also be queried, via {@link
+ * #hasWaitingConsumer}, whether there are any threads waiting for
+ * items, which is a converse analogy to a {@code peek} operation.
+ *
+ * <p>Like other blocking queues, a {@code TransferQueue} may be
+ * capacity bounded. If so, an attempted transfer operation may
+ * initially block waiting for available space, and/or subsequently
+ * block waiting for reception by a consumer. Note that in a queue
+ * with zero capacity, such as {@link SynchronousQueue}, {@code put}
+ * and {@code transfer} are effectively synonymous.
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
+ */
+public interface TransferQueue<E> extends BlockingQueue<E> {
+ /**
+ * Transfers the element to a waiting consumer immediately, if possible.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * otherwise returning {@code false} without enqueuing the element.
+ *
+ * @param e the element to transfer
+ * @return {@code true} if the element was transferred, else
+ * {@code false}
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
+ */
+ boolean tryTransfer(E e);
+
+ /**
+ * Transfers the element to a consumer, waiting if necessary to do so.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * else waits until the element is received by a consumer.
+ *
+ * @param e the element to transfer
+ * @throws InterruptedException if interrupted while waiting,
+ * in which case the element is not left enqueued
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
+ */
+ void transfer(E e) throws InterruptedException;
+
+ /**
+ * Transfers the element to a consumer if it is possible to do so
+ * before the timeout elapses.
+ *
+ * <p>More precisely, transfers the specified element immediately
+ * if there exists a consumer already waiting to receive it (in
+ * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
+ * else waits until the element is received by a consumer,
+ * returning {@code false} if the specified wait time elapses
+ * before the element can be transferred.
+ *
+ * @param e the element to transfer
+ * @param timeout how long to wait before giving up, in units of
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return {@code true} if successful, or {@code false} if
+ * the specified waiting time elapses before completion,
+ * in which case the element is not left enqueued
+ * @throws InterruptedException if interrupted while waiting,
+ * in which case the element is not left enqueued
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
+ */
+ boolean tryTransfer(E e, long timeout, TimeUnit unit)
+ throws InterruptedException;
+
+ /**
+ * Returns {@code true} if there is at least one consumer waiting
+ * to receive an element via {@link #take} or
+ * timed {@link #poll(long,TimeUnit) poll}.
+ * The return value represents a momentary state of affairs.
+ *
+ * @return {@code true} if there is at least one waiting consumer
+ */
+ boolean hasWaitingConsumer();
+
+ /**
+ * Returns an estimate of the number of consumers waiting to
+ * receive elements via {@link #take} or timed
+ * {@link #poll(long,TimeUnit) poll}. The return value is an
+ * approximation of a momentary state of affairs, that may be
+ * inaccurate if consumers have completed or given up waiting.
+ * The value may be useful for monitoring and heuristics, but
+ * not for synchronization control. Implementations of this
+ * method are likely to be noticeably slower than those for
+ * {@link #hasWaitingConsumer}.
+ *
+ * @return the number of consumers waiting to receive elements
+ */
+ int getWaitingConsumerCount();
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
index c774d21..d531f25 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -21,14 +21,14 @@
public class AtomicBoolean implements java.io.Serializable {
private static final long serialVersionUID = 4654671469794556979L;
// setup to use Unsafe.compareAndSwapInt for updates
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicBoolean.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ try {
+ valueOffset = unsafe.objectFieldOffset
+ (AtomicBoolean.class.getDeclaredField("value"));
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
index 16dd568..e0a0018 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -24,14 +24,14 @@
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicInteger.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ try {
+ valueOffset = unsafe.objectFieldOffset
+ (AtomicInteger.class.getDeclaredField("value"));
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
@@ -217,18 +217,33 @@
}
+ /**
+ * Returns the value of this {@code AtomicInteger} as an {@code int}.
+ */
public int intValue() {
return get();
}
+ /**
+ * Returns the value of this {@code AtomicInteger} as a {@code long}
+ * after a widening primitive conversion.
+ */
public long longValue() {
return (long)get();
}
+ /**
+ * Returns the value of this {@code AtomicInteger} as a {@code float}
+ * after a widening primitive conversion.
+ */
public float floatValue() {
return (float)get();
}
+ /**
+ * Returns the value of this {@code AtomicInteger} as a {@code double}
+ * after a widening primitive conversion.
+ */
public double doubleValue() {
return (double)get();
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
index 6dcdfd0..804a51e 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
@@ -1,12 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
import sun.misc.Unsafe;
-import java.util.*;
/**
* An {@code int} array in which elements may be updated atomically.
@@ -19,7 +18,7 @@
public class AtomicIntegerArray implements java.io.Serializable {
private static final long serialVersionUID = 2862133569453604235L;
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);
private static final int shift;
private final int[] array;
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
index e8a0d57..c7ed158 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -236,24 +236,21 @@
* Standard hotspot implementation using intrinsics
*/
private static class AtomicIntegerFieldUpdaterImpl<T> extends AtomicIntegerFieldUpdater<T> {
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private final long offset;
private final Class<T> tclass;
- private final Class cclass;
+ private final Class<?> cclass;
AtomicIntegerFieldUpdaterImpl(Class<T> tclass, String fieldName) {
Field field = null;
- Class caller = null;
+ Class<?> caller = null;
int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
- // BEGIN android-changed
- caller = VMStack.getStackClass2();
- // END android-changed
+ caller = VMStack.getStackClass2(); // android-changed
modifiers = field.getModifiers();
// BEGIN android-removed
- // modifiers = field.getModifiers();
// sun.reflect.misc.ReflectUtil.ensureMemberAccess(
// caller, tclass, null, modifiers);
// sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
@@ -262,7 +259,7 @@
throw new RuntimeException(ex);
}
- Class fieldt = field.getType();
+ Class<?> fieldt = field.getType();
if (fieldt != int.class)
throw new IllegalArgumentException("Must be integer type");
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
index 12d6cd1..5e799f7 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -24,7 +24,7 @@
private static final long serialVersionUID = 1927816293512124184L;
// setup to use Unsafe.compareAndSwapLong for updates
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
/**
@@ -42,10 +42,10 @@
private static native boolean VMSupportsCS8();
static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicLong.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ try {
+ valueOffset = unsafe.objectFieldOffset
+ (AtomicLong.class.getDeclaredField("value"));
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile long value;
@@ -231,18 +231,33 @@
}
+ /**
+ * Returns the value of this {@code AtomicLong} as an {@code int}
+ * after a narrowing primitive conversion.
+ */
public int intValue() {
return (int)get();
}
+ /**
+ * Returns the value of this {@code AtomicLong} as a {@code long}.
+ */
public long longValue() {
return get();
}
+ /**
+ * Returns the value of this {@code AtomicLong} as a {@code float}
+ * after a widening primitive conversion.
+ */
public float floatValue() {
return (float)get();
}
+ /**
+ * Returns the value of this {@code AtomicLong} as a {@code double}
+ * after a widening primitive conversion.
+ */
public double doubleValue() {
return (double)get();
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
index 9e2d25f..22edb3f 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
@@ -1,12 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
import sun.misc.Unsafe;
-import java.util.*;
/**
* A {@code long} array in which elements may be updated atomically.
@@ -18,7 +17,7 @@
public class AtomicLongArray implements java.io.Serializable {
private static final long serialVersionUID = -2308431214976778248L;
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(long[].class);
private static final int shift;
private final long[] array;
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
index 21ef748..748ae69 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -235,14 +235,14 @@
}
private static class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private final long offset;
private final Class<T> tclass;
- private final Class cclass;
+ private final Class<?> cclass;
CASUpdater(Class<T> tclass, String fieldName) {
Field field = null;
- Class caller = null;
+ Class<?> caller = null;
int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
@@ -257,7 +257,7 @@
throw new RuntimeException(ex);
}
- Class fieldt = field.getType();
+ Class<?> fieldt = field.getType();
if (fieldt != long.class)
throw new IllegalArgumentException("Must be long type");
@@ -320,14 +320,14 @@
private static class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private final long offset;
private final Class<T> tclass;
- private final Class cclass;
+ private final Class<?> cclass;
LockedUpdater(Class<T> tclass, String fieldName) {
Field field = null;
- Class caller = null;
+ Class<?> caller = null;
int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
@@ -342,7 +342,7 @@
throw new RuntimeException(ex);
}
- Class fieldt = field.getType();
+ Class<?> fieldt = field.getType();
if (fieldt != long.class)
throw new IllegalArgumentException("Must be long type");
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
index 63f46d6f..eaf700c 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
@@ -1,13 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
-import sun.misc.Unsafe;
-
/**
* An {@code AtomicMarkableReference} maintains an object reference
* along with a mark bit, that can be updated atomically.
@@ -163,7 +161,7 @@
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = UnsafeAccess.THE_ONE; // android-changed
+ private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicMarkableReference.class);
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
index f041bbd..b21e9b6 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -18,14 +18,14 @@
public class AtomicReference<V> implements java.io.Serializable {
private static final long serialVersionUID = -1848883965231344442L;
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicReference.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ try {
+ valueOffset = unsafe.objectFieldOffset
+ (AtomicReference.class.getDeclaredField("value"));
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile V value;
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
index dbc5886..c47728d 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
@@ -1,12 +1,14 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
+
+import java.util.Arrays;
+import java.lang.reflect.Array;
import sun.misc.Unsafe;
-import java.util.*;
/**
* An array of object references in which elements may be updated
@@ -20,13 +22,23 @@
public class AtomicReferenceArray<E> implements java.io.Serializable {
private static final long serialVersionUID = -6209656149925076980L;
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
- private static final int base = unsafe.arrayBaseOffset(Object[].class);
+ private static final Unsafe unsafe;
+ private static final int base;
private static final int shift;
- private final Object[] array;
+ private static final long arrayFieldOffset;
+ private final Object[] array; // must have exact type Object[]
static {
- int scale = unsafe.arrayIndexScale(Object[].class);
+ int scale;
+ try {
+ unsafe = Unsafe.getUnsafe();
+ arrayFieldOffset = unsafe.objectFieldOffset
+ (AtomicReferenceArray.class.getDeclaredField("array"));
+ base = unsafe.arrayBaseOffset(Object[].class);
+ scale = unsafe.arrayIndexScale(Object[].class);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale);
@@ -45,7 +57,7 @@
/**
* Creates a new AtomicReferenceArray of the given length, with all
- * elements initially zero.
+ * elements initially null.
*
* @param length the length of the array
*/
@@ -62,7 +74,7 @@
*/
public AtomicReferenceArray(E[] array) {
// Visibility guaranteed by final field guarantees
- this.array = array.clone();
+ this.array = Arrays.copyOf(array, array.length, Object[].class);
}
/**
@@ -121,7 +133,7 @@
public final E getAndSet(int i, E newValue) {
long offset = checkedByteOffset(i);
while (true) {
- E current = (E) getRaw(offset);
+ E current = getRaw(offset);
if (compareAndSetRaw(offset, current, newValue))
return current;
}
@@ -167,7 +179,7 @@
* @return the String representation of the current values of array
*/
public String toString() {
- int iMax = array.length - 1;
+ int iMax = array.length - 1;
if (iMax == -1)
return "[]";
@@ -181,4 +193,20 @@
}
}
+ /**
+ * Reconstitutes the instance from a stream (that is, deserializes it).
+ * @param s the stream
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException,
+ java.io.InvalidObjectException {
+ // Note: This must be changed if any additional fields are defined
+ Object a = s.readFields().get("array", null);
+ if (a == null || !a.getClass().isArray())
+ throw new java.io.InvalidObjectException("Not array type");
+ if (a.getClass() != Object[].class)
+ a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class);
+ unsafe.putObjectVolatile(this, arrayFieldOffset, a);
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
index 8b3da0b..d23d766 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
@@ -1,11 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
-import dalvik.system.VMStack;
+import dalvik.system.VMStack; // android-added
import sun.misc.Unsafe;
import java.lang.reflect.*;
@@ -155,7 +155,7 @@
private final long offset;
private final Class<T> tclass;
private final Class<V> vclass;
- private final Class cclass;
+ private final Class<?> cclass;
/*
* Internal type checks within all update methods contain
@@ -173,8 +173,8 @@
Class<V> vclass,
String fieldName) {
Field field = null;
- Class fieldClass = null;
- Class caller = null;
+ Class<?> fieldClass = null;
+ Class<?> caller = null;
int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
index 2e826f2..a0cb492 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.atomic;
@@ -162,7 +162,7 @@
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = UnsafeAccess.THE_ONE; // android-changed
+ private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
diff --git a/luni/src/main/java/java/util/concurrent/atomic/Fences.java b/luni/src/main/java/java/util/concurrent/atomic/Fences.java
new file mode 100644
index 0000000..7ecf45a
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/atomic/Fences.java
@@ -0,0 +1,540 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent.atomic;
+
+/**
+ * A set of methods providing fine-grained control over happens-before
+ * and synchronization order relations among reads and/or writes. The
+ * methods of this class are designed for use in uncommon situations
+ * where declaring variables {@code volatile} or {@code final}, using
+ * instances of atomic classes, using {@code synchronized} blocks or
+ * methods, or using other synchronization facilities are not possible
+ * or do not provide the desired control.
+ *
+ * <p><b>Memory Ordering.</b> There are three methods for controlling
+ * ordering relations among memory accesses (i.e., reads and
+ * writes). Method {@code orderWrites} is typically used to enforce
+ * order between two writes, and {@code orderAccesses} between a write
+ * and a read. Method {@code orderReads} is used to enforce order
+ * between two reads with respect to other {@code orderWrites} and/or
+ * {@code orderAccesses} invocations. The formally specified
+ * properties of these methods described below provide
+ * platform-independent guarantees that are honored by all levels of a
+ * platform (compilers, systems, processors). The use of these
+ * methods may result in the suppression of otherwise valid compiler
+ * transformations and optimizations that could visibly violate the
+ * specified orderings, and may or may not entail the use of
+ * processor-level "memory barrier" instructions.
+ *
+ * <p>Each ordering method accepts a {@code ref} argument, and
+ * controls ordering among accesses with respect to this reference.
+ * Invocations must be placed <em>between</em> accesses performed in
+ * expression evaluations and assignment statements to control the
+ * orderings of prior versus subsequent accesses appearing in program
+ * order. These methods also return their arguments to simplify
+ * correct usage in these contexts.
+ *
+ * <p>Usages of ordering methods almost always take one of the forms
+ * illustrated in the examples below. These idioms arrange some of
+ * the ordering properties associated with {@code volatile} and
+ * related language-based constructions, but without other
+ * compile-time and runtime benefits that make language-based
+ * constructions far better choices when they are applicable. Usages
+ * should be restricted to the control of strictly internal
+ * implementation matters inside a class or package, and must either
+ * avoid or document any consequent violations of ordering or safety
+ * properties expected by users of a class employing them.
+ *
+ * <p><b>Reachability.</b> Method {@code reachabilityFence}
+ * establishes an ordering for strong reachability (as defined in the
+ * {@link java.lang.ref} package specification) with respect to
+ * garbage collection. Method {@code reachabilityFence} differs from
+ * the others in that it controls relations that are otherwise only
+ * implicit in a program -- the reachability conditions triggering
+ * garbage collection. As illustrated in the sample usages below,
+ * this method is applicable only when reclamation may have visible
+ * effects, which is possible for objects with finalizers (see Section
+ * 12.6 of the Java Language Specification) that are implemented in
+ * ways that rely on ordering control for correctness.
+ *
+ * <p><b>Sample Usages</b>
+ *
+ * <p><b>Safe publication.</b> With care, method {@code orderWrites}
+ * may be used to obtain the memory safety effects of {@code final}
+ * for a field that cannot be declared as {@code final}, because its
+ * primary initialization cannot be performed in a constructor, in
+ * turn because it is used in a framework requiring that all classes
+ * have a no-argument constructor; as in:
+ *
+ * <pre> {@code
+ * class WidgetHolder {
+ * private Widget widget;
+ * public WidgetHolder() {}
+ * public static WidgetHolder newWidgetHolder(Params params) {
+ * WidgetHolder h = new WidgetHolder();
+ * h.widget = new Widget(params);
+ * return Fences.orderWrites(h);
+ * }
+ * }}</pre>
+ *
+ * Here, the invocation of {@code orderWrites} ensures that the
+ * effects of the widget assignment are ordered before those of any
+ * (unknown) subsequent stores of {@code h} in other variables that
+ * make {@code h} available for use by other objects. Initialization
+ * sequences using {@code orderWrites} require more care than those
+ * involving {@code final} fields. When {@code final} is not used,
+ * compilers cannot help you to ensure that the field is set correctly
+ * across all usages. You must fully initialize objects
+ * <em>before</em> the {@code orderWrites} invocation that makes
+ * references to them safe to assign to accessible variables. Further,
+ * initialization sequences must not internally "leak" the reference
+ * by using it as an argument to a callback method or adding it to a
+ * static data structure. If less constrained usages were required,
+ * it may be possible to cope using more extensive sets of fences, or
+ * as a normally better choice, using synchronization (locking).
+ * Conversely, if it were possible to do so, the best option would be
+ * to rewrite class {@code WidgetHolder} to use {@code final}.
+ *
+ * <p>An alternative approach is to place similar mechanics in the
+ * (sole) method that makes such objects available for use by others.
+ * Here is a stripped-down example illustrating the essentials. In
+ * practice, among other changes, you would use access methods instead
+ * of a public field.
+ *
+ * <pre> {@code
+ * class AnotherWidgetHolder {
+ * public Widget widget;
+ * void publish(Widget w) {
+ * this.widget = Fences.orderWrites(w);
+ * }
+ * // ...
+ * }}</pre>
+ *
+ * In this case, the {@code orderWrites} invocation occurs before the
+ * store making the object available. Correctness again relies on
+ * ensuring that there are no leaks prior to invoking this method, and
+ * that it really is the <em>only</em> means of accessing the
+ * published object. This approach is not often applicable --
+ * normally you would publish objects using a thread-safe collection
+ * that itself guarantees the expected ordering relations. However, it
+ * may come into play in the construction of such classes themselves.
+ *
+ * <p><b>Safely updating fields.</b> Outside of the initialization
+ * idioms illustrated above, Fence methods ordering writes must be
+ * paired with those ordering reads. To illustrate, suppose class
+ * {@code c} contains an accessible variable {@code data} that should
+ * have been declared as {@code volatile} but wasn't:
+ *
+ * <pre> {@code
+ * class C {
+ * Object data; // need volatile access but not volatile
+ * // ...
+ * }
+ *
+ * class App {
+ * Object getData(C c) {
+ * return Fences.orderReads(c).data;
+ * }
+ *
+ * void setData(C c) {
+ * Object newValue = ...;
+ * c.data = Fences.orderWrites(newValue);
+ * Fences.orderAccesses(c);
+ * }
+ * // ...
+ * }}</pre>
+ *
+ * Method {@code getData} provides an emulation of {@code volatile}
+ * reads of (non-long/double) fields by ensuring that the read of
+ * {@code c} obtained as an argument is ordered before subsequent
+ * reads using this reference, and then performs the read of its
+ * field. Method {@code setData} provides an emulation of volatile
+ * writes, ensuring that all other relevant writes have completed,
+ * then performing the assignment, and then ensuring that the write is
+ * ordered before any other access. These techniques may apply even
+ * when fields are not directly accessible, in which case calls to
+ * fence methods would surround calls to methods such as {@code
+ * c.getData()}. However, these techniques cannot be applied to
+ * {@code long} or {@code double} fields because reads and writes of
+ * fields of these types are not guaranteed to be
+ * atomic. Additionally, correctness may require that all accesses of
+ * such data use these kinds of wrapper methods, which you would need
+ * to manually ensure.
+ *
+ * <p>More generally, Fence methods can be used in this way to achieve
+ * the safety properties of {@code volatile}. However their use does
+ * not necessarily guarantee the full sequential consistency
+ * properties specified in the Java Language Specification chapter 17
+ * for programs using {@code volatile}. In particular, emulation using
+ * Fence methods is not guaranteed to maintain the property that
+ * {@code volatile} operations performed by different threads are
+ * observed in the same order by all observer threads.
+ *
+ * <p><b>Acquire/Release management of threadsafe objects</b>. It may
+ * be possible to use weaker conventions for volatile-like variables
+ * when they are used to keep track of objects that fully manage their
+ * own thread-safety and synchronization. Here, an acquiring read
+ * operation remains the same as a volatile-read, but a releasing
+ * write differs by virtue of not itself ensuring an ordering of its
+ * write with subsequent reads, because the required effects are
+ * already ensured by the referenced objects.
+ * For example:
+ *
+ * <pre> {@code
+ * class Item {
+ * synchronized f(); // ALL methods are synchronized
+ * // ...
+ * }
+ *
+ * class ItemHolder {
+ * private Item item;
+ * Item acquireItem() {
+ * return Fences.orderReads(item);
+ * }
+ *
+ * void releaseItem(Item x) {
+ * item = Fences.orderWrites(x);
+ * }
+ *
+ * // ...
+ * }}</pre>
+ *
+ * Because this construction avoids use of {@code orderAccesses},
+ * which is typically more costly than the other fence methods, it may
+ * result in better performance than using {@code volatile} or its
+ * emulation. However, as is the case with most applications of fence
+ * methods, correctness relies on the usage context -- here, the
+ * thread safety of {@code Item}, as well as the lack of need for full
+ * volatile semantics inside this class itself. However, the second
+ * concern means that it can be difficult to extend the {@code
+ * ItemHolder} class in this example to be more useful.
+ *
+ * <p><b>Avoiding premature finalization.</b> Finalization may occur
+ * whenever a Java Virtual Machine detects that no reference to an
+ * object will ever be stored in the heap: A garbage collector may
+ * reclaim an object even if the fields of that object are still in
+ * use, so long as the object has otherwise become unreachable. This
+ * may have surprising and undesirable effects in cases such as the
+ * following example in which the bookkeeping associated with a class
+ * is managed through array indices. Here, method {@code action}
+ * uses a {@code reachabilityFence} to ensure that the Resource
+ * object is not reclaimed before bookkeeping on an associated
+ * ExternalResource has been performed; in particular here, to ensure
+ * that the array slot holding the ExternalResource is not nulled out
+ * in method {@link Object#finalize}, which may otherwise run
+ * concurrently.
+ *
+ * <pre> {@code
+ * class Resource {
+ * private static ExternalResource[] externalResourceArray = ...
+ *
+ * int myIndex;
+ * Resource(...) {
+ * myIndex = ...
+ * externalResourceArray[myIndex] = ...;
+ * ...
+ * }
+ * protected void finalize() {
+ * externalResourceArray[myIndex] = null;
+ * ...
+ * }
+ * public void action() {
+ * try {
+ * // ...
+ * int i = myIndex;
+ * Resource.update(externalResourceArray[i]);
+ * } finally {
+ * Fences.reachabilityFence(this);
+ * }
+ * }
+ * private static void update(ExternalResource ext) {
+ * ext.status = ...;
+ * }
+ * }}</pre>
+ *
+ * Here, the call to {@code reachabilityFence} is nonintuitively
+ * placed <em>after</em> the call to {@code update}, to ensure that
+ * the array slot is not nulled out by {@link Object#finalize} before
+ * the update, even if the call to {@code action} was the last use of
+ * this object. This might be the case if for example a usage in a
+ * user program had the form {@code new Resource().action();} which
+ * retains no other reference to this Resource. While probably
+ * overkill here, {@code reachabilityFence} is placed in a {@code
+ * finally} block to ensure that it is invoked across all paths in the
+ * method. In a method with more complex control paths, you might
+ * need further precautions to ensure that {@code reachabilityFence}
+ * is encountered along all of them.
+ *
+ * <p>It is sometimes possible to better encapsulate use of
+ * {@code reachabilityFence}. Continuing the above example, if it
+ * were OK for the call to method update to proceed even if the
+ * finalizer had already executed (nulling out slot), then you could
+ * localize use of {@code reachabilityFence}:
+ *
+ * <pre> {@code
+ * public void action2() {
+ * // ...
+ * Resource.update(getExternalResource());
+ * }
+ * private ExternalResource getExternalResource() {
+ * ExternalResource ext = externalResourceArray[myIndex];
+ * Fences.reachabilityFence(this);
+ * return ext;
+ * }}</pre>
+ *
+ * <p>Method {@code reachabilityFence} is not required in
+ * constructions that themselves ensure reachability. For example,
+ * because objects that are locked cannot in general be reclaimed, it
+ * would suffice if all accesses of the object, in all methods of
+ * class Resource (including {@code finalize}) were enclosed in {@code
+ * synchronized (this)} blocks. (Further, such blocks must not include
+ * infinite loops, or themselves be unreachable, which fall into the
+ * corner case exceptions to the "in general" disclaimer.) However,
+ * method {@code reachabilityFence} remains a better option in cases
+ * where this approach is not as efficient, desirable, or possible;
+ * for example because it would encounter deadlock.
+ *
+ * <p><b>Formal Properties.</b>
+ *
+ * <p>Using the terminology of The Java Language Specification chapter
+ * 17, the rules governing the semantics of the methods of this class
+ * are as follows:
+ *
+ * <p> The following is still under construction.
+ *
+ * <dl>
+ *
+ * <dt><b>[Definitions]</b>
+ * <dd>
+ * <ul>
+ *
+ * <li>Define <em>sequenced(a, b)</em> to be true if <em>a</em>
+ * occurs before <em>b</em> in <em>program order</em>.
+ *
+ * <li>Define <em>accesses(a, p)</em> to be true if
+ * <em>a</em> is a read or write of a field (or if an array, an
+ * element) of the object referenced by <em>p</em>.
+ *
+ * <li>Define <em>deeplyAccesses(a, p)</em> to be true if either
+ * <em>accesses(a, p)</em> or <em>deeplyAccesses(a, q)</em> where
+ * <em>q</em> is the value seen by some read <em>r</em>
+ * such that <em>accesses(r, p)</em>.
+ *
+ * </ul>
+ * <p>
+ * <dt><b>[Matching]</b>
+ * <dd> Given:
+ *
+ * <ul>
+ *
+ * <li><em>p</em>, a reference to an object
+ *
+ * <li><em>wf</em>, an invocation of {@code orderWrites(p)} or
+ * {@code orderAccesses(p)}
+ *
+ * <li><em>w</em>, a write of value <em>p</em>
+ *
+ * <li> <em>rf</em>, an invocation of {@code orderReads(p)} or
+ * {@code orderAccesses(p)}
+ *
+ * <li> <em>r</em>, a read returning value <em>p</em>
+ *
+ * </ul>
+ * If:
+ * <ul>
+ * <li>sequenced(wf, w)
+ * <li>read <em>r</em> sees write <em>w</em>
+ * <li>sequenced(r, rf)
+ * </ul>
+ * Then:
+ * <ul>
+ *
+ * <li> <em>wf happens-before rf</em>
+ *
+ * <li> <em>wf</em> precedes <em>rf</em> in the
+ * <em>synchronization order</em>
+ *
+ * <li> If (<em>r1</em>, <em>w1</em>) and (<em>r2</em>,
+ * <em>w2</em>) are two pairs of reads and writes, both
+ * respectively satisfying the above conditions for <em>p</em>,
+ * and sequenced(r1, r2) then it is not the case that <em>w2
+ * happens-before w1</em>.
+ *
+ * </ul>
+ * <p>
+ * <dt><b>[Initial Reads]</b>
+ * <dd> Given:
+ *
+ * <ul>
+ *
+ * <li><em>p</em>, a reference to an object
+ *
+ * <li> <em>a</em>, an access where deeplyAccesses(a, p)
+ *
+ * <li><em>wf</em>, an invocation of {@code orderWrites(p)} or
+ * {@code orderAccesses(p)}
+ *
+ * <li><em>w</em>, a write of value <em>p</em>
+ *
+ * <li> <em>r</em>, a read returning value <em>p</em>
+ *
+ * <li> <em>b</em>, an access where accesses(b, p)
+ *
+ * </ul>
+ * If:
+ * <ul>
+ * <li>sequenced(a, wf);
+ * <li>sequenced(wf, w)
+ * <li>read <em>r</em> sees write <em>w</em>, and
+ * <em>r</em> is the first read by some thread
+ * <em>t</em> that sees value <em>p</em>
+ * <li>sequenced(r, b)
+ * </ul>
+ * Then:
+ * <ul>
+ * <li> the effects of <em>b</em> are constrained
+ * by the relation <em>a happens-before b</em>.
+ * </ul>
+ * <p>
+ * <dt><b>[orderAccesses]</b>
+ * <dd> Given:
+ *
+ * <ul>
+ * <li><em>p</em>, a reference to an object
+ * <li><em>f</em>, an invocation of {@code orderAccesses(p)}
+ * </ul>
+ * If:
+ * <ul>
+ * <li>sequenced(f, w)
+ * </ul>
+ *
+ * Then:
+ *
+ * <ul>
+ *
+ * <li> <em>f</em> is an element of the <em>synchronization order</em>.
+ *
+ * </ul>
+ * <p>
+ * <dt><b>[Reachability]</b>
+ * <dd> Given:
+ *
+ * <ul>
+ *
+ * <li><em>p</em>, a reference to an object
+ *
+ * <li><em>f</em>, an invocation of {@code reachabilityFence(p)}
+ *
+ * <li><em>a</em>, an access where accesses(a, p)
+ *
+ * <li><em>b</em>, an action (by a garbage collector) taking
+ * the form of an invocation of {@code
+ * p.finalize()} or of enqueing any {@link
+ * java.lang.ref.Reference} constructed with argument <em>p</em>
+ *
+ * </ul>
+ *
+ * If:
+ * <ul>
+ * <li>sequenced(a, f)
+ * </ul>
+ *
+ * Then:
+ *
+ * <ul>
+ *
+ * <li> <em>a happens-before b</em>.
+ *
+ * </ul>
+ *
+ * </dl>
+ *
+ * @since 1.7
+ * @hide
+ * @author Doug Lea
+ */
+public class Fences {
+ private Fences() {} // Non-instantiable
+
+ /*
+ * The methods of this class are intended to be intrinisified by a
+ * JVM. However, we provide correct but inefficient Java-level
+ * code that simply reads and writes a static volatile
+ * variable. Without JVM support, the consistency effects are
+ * stronger than necessary, and the memory contention effects can
+ * be a serious performance issue.
+ */
+ private static volatile int theVolatile;
+
+ /**
+ * Informally: Ensures that a read of the given reference prior to
+ * the invocation of this method occurs before a subsequent use of
+ * the given reference with the effect of reading or writing a
+ * field (or if an array, element) of the referenced object. The
+ * use of this method is sensible only when paired with other
+ * invocations of {@link #orderWrites} and/or {@link
+ * #orderAccesses} for the given reference. For details, see the
+ * class documentation for this class.
+ *
+ * @param ref the reference. If null, this method has no effect.
+ * @return the given ref, to simplify usage
+ */
+ public static <T> T orderReads(T ref) {
+ int ignore = theVolatile;
+ return ref;
+ }
+
+ /**
+ * Informally: Ensures that a use of the given reference with the
+ * effect of reading or writing a field (or if an array, element)
+ * of the referenced object, prior to the invocation of this
+ * method occur before a subsequent write of the reference. For
+ * details, see the class documentation for this class.
+ *
+ * @param ref the reference. If null, this method has no effect.
+ * @return the given ref, to simplify usage
+ */
+ public static <T> T orderWrites(T ref) {
+ theVolatile = 0;
+ return ref;
+ }
+
+ /**
+ * Informally: Ensures that accesses (reads or writes) using the
+ * given reference prior to the invocation of this method occur
+ * before subsequent accesses. For details, see the class
+ * documentation for this class.
+ *
+ * @param ref the reference. If null, this method has no effect.
+ * @return the given ref, to simplify usage
+ */
+ public static <T> T orderAccesses(T ref) {
+ theVolatile = 0;
+ return ref;
+ }
+
+ /**
+ * Ensures that the object referenced by the given reference
+ * remains <em>strongly reachable</em> (as defined in the {@link
+ * java.lang.ref} package documentation), regardless of any prior
+ * actions of the program that might otherwise cause the object to
+ * become unreachable; thus, the referenced object is not
+ * reclaimable by garbage collection at least until after the
+ * invocation of this method. Invocation of this method does not
+ * itself initiate garbage collection or finalization.
+ *
+ * <p>See the class-level documentation for further explanation
+ * and usage examples.
+ *
+ * @param ref the reference. If null, this method has no effect.
+ */
+ public static void reachabilityFence(Object ref) {
+ if (ref != null) {
+ synchronized (ref) {}
+ }
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java b/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java
deleted file mode 100644
index 96fff17..0000000
--- a/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.util.concurrent.atomic;
-
-import sun.misc.Unsafe;
-
-/**
- * Easy access to {@link Unsafe} for the rest of this package.
- */
-/*package*/ final class UnsafeAccess {
- /** non-null; unique instance of {@link Unsafe} */
- /*package*/ static final Unsafe THE_ONE = Unsafe.getUnsafe();
-
- /**
- * This class is uninstantiable.
- */
- private UnsafeAccess() {
- // This space intentionally left blank.
- }
-}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/package-info.java b/luni/src/main/java/java/util/concurrent/atomic/package-info.java
index 4a4375d..efbb413 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/package-info.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/package-info.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
@@ -11,9 +11,7 @@
* array elements to those that also provide an atomic conditional update
* operation of the form:
*
- * <pre>
- * boolean compareAndSet(expectedValue, updateValue);
- * </pre>
+ * <pre> {@code boolean compareAndSet(expectedValue, updateValue);}</pre>
*
* <p>This method (which varies in argument types across different
* classes) atomically sets a variable to the {@code updateValue} if it
@@ -40,15 +38,30 @@
* {@code AtomicInteger} provide atomic increment methods. One
* application is to generate sequence numbers, as in:
*
- * <pre>
+ * <pre> {@code
* class Sequencer {
* private final AtomicLong sequenceNumber
* = new AtomicLong(0);
* public long next() {
* return sequenceNumber.getAndIncrement();
* }
- * }
- * </pre>
+ * }}</pre>
+ *
+ * <p>It is straightforward to define new utility functions that, like
+ * {@code getAndIncrement}, apply a function to a value atomically.
+ * For example, given some transformation
+ * <pre> {@code long transform(long input)}</pre>
+ *
+ * write your utility method as follows:
+ * <pre> {@code
+ * boolean getAndTransform(AtomicLong var) {
+ * while (true) {
+ * long current = var.get();
+ * long next = transform(current);
+ * if (var.compareAndSet(current, next))
+ * return current;
+ * }
+ * }}</pre>
*
* <p>The memory effects for accesses and updates of atomics generally
* follow the rules for volatiles, as stated in
@@ -161,9 +174,9 @@
* {@code byte} values, and cast appropriately.
*
* You can also hold floats using
- * {@link java.lang.Float#floatToIntBits} and
+ * {@link java.lang.Float#floatToRawIntBits} and
* {@link java.lang.Float#intBitsToFloat} conversions, and doubles using
- * {@link java.lang.Double#doubleToLongBits} and
+ * {@link java.lang.Double#doubleToRawLongBits} and
* {@link java.lang.Double#longBitsToDouble} conversions.
*
* @since 1.5
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java
index f3780e5..4bec0cf 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
index 5c8111c..7b36460 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
@@ -1,13 +1,12 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.*;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
import sun.misc.Unsafe;
/**
@@ -569,7 +568,7 @@
/**
* Convenience method to interrupt current thread.
*/
- private static void selfInterrupt() {
+ static void selfInterrupt() {
Thread.currentThread().interrupt();
}
@@ -1231,7 +1230,7 @@
* due to the queue being empty.
*
* <p>This method is designed to be used by a fair synchronizer to
- * avoid <a href="AbstractQueuedSynchronizer#barging">barging</a>.
+ * avoid <a href="AbstractQueuedSynchronizer.html#barging">barging</a>.
* Such a synchronizer's {@link #tryAcquire} method should return
* {@code false}, and its {@link #tryAcquireShared} method should
* return a negative value, if this method returns {@code true}
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
index 065f130..42029f0 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
@@ -1,13 +1,12 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.*;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
import sun.misc.Unsafe;
// BEGIN android-note
@@ -173,7 +172,7 @@
* It also supports conditions and exposes
* one of the instrumentation methods:
*
- * <pre>
+ * <pre> {@code
* class Mutex implements Lock, java.io.Serializable {
*
* // Our internal helper class
@@ -229,15 +228,14 @@
* throws InterruptedException {
* return sync.tryAcquireNanos(1, unit.toNanos(timeout));
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>Here is a latch class that is like a {@link CountDownLatch}
* except that it only requires a single <tt>signal</tt> to
* fire. Because a latch is non-exclusive, it uses the <tt>shared</tt>
* acquire and release methods.
*
- * <pre>
+ * <pre> {@code
* class BooleanLatch {
*
* private static class Sync extends AbstractQueuedSynchronizer {
@@ -259,8 +257,7 @@
* public void await() throws InterruptedException {
* sync.acquireSharedInterruptibly(1);
* }
- * }
- * </pre>
+ * }}</pre>
*
* @since 1.5
* @author Doug Lea
@@ -800,7 +797,7 @@
/**
* Convenience method to interrupt current thread.
*/
- private static void selfInterrupt() {
+ static void selfInterrupt() {
Thread.currentThread().interrupt();
}
@@ -2251,9 +2248,7 @@
* are at it, we do the same for other CASable fields (which could
* otherwise be done with atomic field updaters).
*/
- // BEGIN android-changed
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE;
- // END android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
diff --git a/luni/src/main/java/java/util/concurrent/locks/Condition.java b/luni/src/main/java/java/util/concurrent/locks/Condition.java
index 8504e1f..7050df9 100644
--- a/luni/src/main/java/java/util/concurrent/locks/Condition.java
+++ b/luni/src/main/java/java/util/concurrent/locks/Condition.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
@@ -331,10 +331,9 @@
/**
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified waiting time elapses. This method is behaviorally
- * equivalent to:<br>
- * <pre>
- * awaitNanos(unit.toNanos(time)) > 0
- * </pre>
+ * equivalent to:
+ * <pre> {@code awaitNanos(unit.toNanos(time)) > 0}</pre>
+ *
* @param time the maximum time to wait
* @param unit the time unit of the {@code time} argument
* @return {@code false} if the waiting time detectably elapsed
diff --git a/luni/src/main/java/java/util/concurrent/locks/Lock.java b/luni/src/main/java/java/util/concurrent/locks/Lock.java
index 4b9abd6..d5c6294 100644
--- a/luni/src/main/java/java/util/concurrent/locks/Lock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/Lock.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
@@ -48,14 +48,14 @@
* methods and statements. In most cases, the following idiom
* should be used:
*
- * <pre><tt> Lock l = ...;
- * l.lock();
- * try {
- * // access the resource protected by this lock
- * } finally {
- * l.unlock();
- * }
- * </tt></pre>
+ * <pre> {@code
+ * Lock l = ...;
+ * l.lock();
+ * try {
+ * // access the resource protected by this lock
+ * } finally {
+ * l.unlock();
+ * }}</pre>
*
* When locking and unlocking occur in different scopes, care must be
* taken to ensure that all code that is executed while the lock is
@@ -210,18 +210,18 @@
* immediately with the value {@code false}.
*
* <p>A typical usage idiom for this method would be:
- * <pre>
- * Lock lock = ...;
- * if (lock.tryLock()) {
- * try {
- * // manipulate protected state
- * } finally {
- * lock.unlock();
- * }
- * } else {
- * // perform alternative actions
- * }
- * </pre>
+ * <pre> {@code
+ * Lock lock = ...;
+ * if (lock.tryLock()) {
+ * try {
+ * // manipulate protected state
+ * } finally {
+ * lock.unlock();
+ * }
+ * } else {
+ * // perform alternative actions
+ * }}</pre>
+ *
* This usage ensures that the lock is unlocked if it was acquired, and
* doesn't try to unlock if the lock was not acquired.
*
diff --git a/luni/src/main/java/java/util/concurrent/locks/LockSupport.java b/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
index 0c0f07d..422e428 100644
--- a/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
+++ b/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
@@ -1,11 +1,10 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
-import java.util.concurrent.*;
import sun.misc.Unsafe;
@@ -49,7 +48,10 @@
* higher-level synchronization utilities, and are not in themselves
* useful for most concurrency control applications. The {@code park}
* method is designed for use only in constructions of the form:
- * <pre>while (!canProceed()) { ... LockSupport.park(this); }</pre>
+ *
+ * <pre> {@code
+ * while (!canProceed()) { ... LockSupport.park(this); }}</pre>
+ *
* where neither {@code canProceed} nor any other actions prior to the
* call to {@code park} entail locking or blocking. Because only one
* permit is associated with each thread, any intermediary uses of
@@ -57,7 +59,7 @@
*
* <p><b>Sample Usage.</b> Here is a sketch of a first-in-first-out
* non-reentrant lock class:
- * <pre>{@code
+ * <pre> {@code
* class FIFOMutex {
* private final AtomicBoolean locked = new AtomicBoolean(false);
* private final Queue<Thread> waiters
@@ -92,7 +94,7 @@
private LockSupport() {} // Cannot be instantiated.
// Hotspot implementation via intrinsics API
- private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long parkBlockerOffset;
static {
@@ -246,10 +248,14 @@
* snapshot -- the thread may have since unblocked or blocked on a
* different blocker object.
*
+ * @param t the thread
* @return the blocker
+ * @throws NullPointerException if argument is null
* @since 1.6
*/
public static Object getBlocker(Thread t) {
+ if (t == null)
+ throw new NullPointerException();
return unsafe.getObjectVolatile(t, parkBlockerOffset);
}
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java b/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
index 484f68d..bb7b388 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java b/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
index cf787ca..07baf41 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
@@ -1,13 +1,12 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.*;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
/**
* A reentrant mutual exclusion {@link Lock} with the same basic
@@ -44,7 +43,7 @@
* follow a call to {@code lock} with a {@code try} block, most
* typically in a before/after construction such as:
*
- * <pre>
+ * <pre> {@code
* class X {
* private final ReentrantLock lock = new ReentrantLock();
* // ...
@@ -57,8 +56,7 @@
* lock.unlock()
* }
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>In addition to implementing the {@link Lock} interface, this
* class defines methods {@code isLocked} and
@@ -354,8 +352,11 @@
* method. If you want a timed {@code tryLock} that does permit barging on
* a fair lock then combine the timed and un-timed forms together:
*
- * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
- * </pre>
+ * <pre> {@code
+ * if (lock.tryLock() ||
+ * lock.tryLock(timeout, unit)) {
+ * ...
+ * }}</pre>
*
* <p>If the current thread
* already holds this lock then the hold count is incremented by one and
@@ -485,7 +486,7 @@
* not be entered with the lock already held then we can assert that
* fact:
*
- * <pre>
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -498,8 +499,7 @@
* lock.unlock();
* }
* }
- * }
- * </pre>
+ * }}</pre>
*
* @return the number of holds on this lock by the current thread,
* or zero if this lock is not held by the current thread
@@ -516,7 +516,7 @@
* testing. For example, a method that should only be called while
* a lock is held can assert that this is the case:
*
- * <pre>
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -525,13 +525,12 @@
* assert lock.isHeldByCurrentThread();
* // ... method body
* }
- * }
- * </pre>
+ * }}</pre>
*
* <p>It can also be used to ensure that a reentrant lock is used
* in a non-reentrant manner, for example:
*
- * <pre>
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -545,8 +544,7 @@
* lock.unlock();
* }
* }
- * }
- * </pre>
+ * }}</pre>
*
* @return {@code true} if current thread holds this lock and
* {@code false} otherwise
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java b/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
index b1a1a60..244a4a7 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
@@ -1,12 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
import java.util.*;
/**
@@ -33,7 +32,7 @@
* <dt><b><i>Fair mode</i></b>
* <dd> When constructed as fair, threads contend for entry using an
* approximately arrival-order policy. When the currently held lock
- * is released either the longest-waiting single writer thread will
+ * is released, either the longest-waiting single writer thread will
* be assigned the write lock, or if there is a group of reader threads
* waiting longer than all waiting writer threads, that group will be
* assigned the read lock.
@@ -51,8 +50,8 @@
* will block unless both the read lock and write lock are free (which
* implies there are no waiting threads). (Note that the non-blocking
* {@link ReadLock#tryLock()} and {@link WriteLock#tryLock()} methods
- * do not honor this fair setting and will acquire the lock if it is
- * possible, regardless of waiting threads.)
+ * do not honor this fair setting and will immediately acquire the lock
+ * if it is possible, regardless of waiting threads.)
* <p>
* </dl>
*
@@ -114,21 +113,21 @@
* void processCachedData() {
* rwl.readLock().lock();
* if (!cacheValid) {
- * // Must release read lock before acquiring write lock
- * rwl.readLock().unlock();
- * rwl.writeLock().lock();
- * try {
- * // Recheck state because another thread might have
- * // acquired write lock and changed state before we did.
- * if (!cacheValid) {
- * data = ...
- * cacheValid = true;
- * }
- * // Downgrade by acquiring read lock before releasing write lock
- * rwl.readLock().lock();
- * } finally {
- * rwl.writeLock().unlock(); // Unlock write, still hold read
- * }
+ * // Must release read lock before acquiring write lock
+ * rwl.readLock().unlock();
+ * rwl.writeLock().lock();
+ * try {
+ * // Recheck state because another thread might have
+ * // acquired write lock and changed state before we did.
+ * if (!cacheValid) {
+ * data = ...
+ * cacheValid = true;
+ * }
+ * // Downgrade by acquiring read lock before releasing write lock
+ * rwl.readLock().lock();
+ * } finally {
+ * rwl.writeLock().unlock(); // Unlock write, still hold read
+ * }
* }
*
* try {
@@ -147,33 +146,33 @@
* is a class using a TreeMap that is expected to be large and
* concurrently accessed.
*
- * <pre>{@code
+ * <pre> {@code
* class RWDictionary {
- * private final Map<String, Data> m = new TreeMap<String, Data>();
- * private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
- * private final Lock r = rwl.readLock();
- * private final Lock w = rwl.writeLock();
+ * private final Map<String, Data> m = new TreeMap<String, Data>();
+ * private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ * private final Lock r = rwl.readLock();
+ * private final Lock w = rwl.writeLock();
*
- * public Data get(String key) {
- * r.lock();
- * try { return m.get(key); }
- * finally { r.unlock(); }
- * }
- * public String[] allKeys() {
- * r.lock();
- * try { return m.keySet().toArray(); }
- * finally { r.unlock(); }
- * }
- * public Data put(String key, Data value) {
- * w.lock();
- * try { return m.put(key, value); }
- * finally { w.unlock(); }
- * }
- * public void clear() {
- * w.lock();
- * try { m.clear(); }
- * finally { w.unlock(); }
- * }
+ * public Data get(String key) {
+ * r.lock();
+ * try { return m.get(key); }
+ * finally { r.unlock(); }
+ * }
+ * public String[] allKeys() {
+ * r.lock();
+ * try { return m.keySet().toArray(); }
+ * finally { r.unlock(); }
+ * }
+ * public Data put(String key, Data value) {
+ * w.lock();
+ * try { return m.put(key, value); }
+ * finally { w.unlock(); }
+ * }
+ * public void clear() {
+ * w.lock();
+ * try { m.clear(); }
+ * finally { w.unlock(); }
+ * }
* }}</pre>
*
* <h3>Implementation Notes</h3>
@@ -625,7 +624,9 @@
}
/**
- * Reconstitute this lock instance from a stream
+ * Reconstitutes this lock instance from a stream (that is,
+ * deserializes it).
+ *
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
@@ -790,8 +791,11 @@
* permit barging on a fair lock then combine the timed and
* un-timed forms together:
*
- * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
- * </pre>
+ * <pre> {@code
+ * if (lock.tryLock() ||
+ * lock.tryLock(timeout, unit)) {
+ * ...
+ * }}</pre>
*
* <p>If the write lock is held by another thread then the
* current thread becomes disabled for thread scheduling
@@ -1020,8 +1024,11 @@
* that does permit barging on a fair lock then combine the
* timed and un-timed forms together:
*
- * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
- * </pre>
+ * <pre> {@code
+ * if (lock.tryLock() ||
+ * lock.tryLock(timeout, unit)) {
+ * ...
+ * }}</pre>
*
* <p>If the current thread already holds this lock then the
* hold count is incremented by one and the method returns
diff --git a/luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java b/luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java
deleted file mode 100644
index 07f64e4..0000000
--- a/luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.util.concurrent.locks;
-
-import sun.misc.Unsafe;
-
-/**
- * Easy access to {@link Unsafe} for the rest of this package.
- */
-/*package*/ final class UnsafeAccess {
- /** non-null; unique instance of {@link Unsafe} */
- /*package*/ static final Unsafe THE_ONE = Unsafe.getUnsafe();
-
- /**
- * This class is uninstantiable.
- */
- private UnsafeAccess() {
- // This space intentionally left blank.
- }
-}
diff --git a/luni/src/main/java/java/util/concurrent/locks/package-info.java b/luni/src/main/java/java/util/concurrent/locks/package-info.java
index 860acdd..433f869 100644
--- a/luni/src/main/java/java/util/concurrent/locks/package-info.java
+++ b/luni/src/main/java/java/util/concurrent/locks/package-info.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
diff --git a/luni/src/main/java/java/util/concurrent/package-info.java b/luni/src/main/java/java/util/concurrent/package-info.java
index 8509a41..155d1b8 100644
--- a/luni/src/main/java/java/util/concurrent/package-info.java
+++ b/luni/src/main/java/java/util/concurrent/package-info.java
@@ -1,11 +1,11 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
+ * http://creativecommons.org/publicdomain/zero/1.0/
*/
// BEGIN android-note
-// Dropped references to unreleased APIs (ForkJoinPool, Phaser, etc.)
+// omit links to ForkJoinPool, ForkJoinTask, LinkedTransferQueue, PHaser, TransferQueue
// END android-note
/**
@@ -111,7 +111,7 @@
*
* <h2>Synchronizers</h2>
*
- * Five classes aid common special-purpose synchronization idioms.
+ * Four classes aid common special-purpose synchronization idioms.
* <ul>
*
* <li>{@link java.util.concurrent.Semaphore} is a classic concurrency tool.
@@ -146,7 +146,7 @@
* A {@code CopyOnWriteArrayList} is preferable to a synchronized
* {@code ArrayList} when the expected number of reads and traversals
* greatly outnumber the number of updates to a list.
-
+ *
* <p>The "Concurrent" prefix used with some classes in this package
* is a shorthand indicating several differences from similar
* "synchronized" classes. For example {@code java.util.Hashtable} and
@@ -243,7 +243,8 @@
* in each thread <i>happen-before</i> those subsequent to the
* corresponding {@code exchange()} in another thread.
*
- * <li>Actions prior to calling {@code CyclicBarrier.await}
+ * <li>Actions prior to calling {@code CyclicBarrier.await} and
+ * {@code Phaser.awaitAdvance} (as well as its variants)
* <i>happen-before</i> actions performed by the barrier action, and
* actions performed by the barrier action <i>happen-before</i> actions
* subsequent to a successful return from the corresponding {@code await}
diff --git a/luni/src/main/java/java/util/jar/JarOutputStream.java b/luni/src/main/java/java/util/jar/JarOutputStream.java
index 78e3aa5..fc0645e 100644
--- a/luni/src/main/java/java/util/jar/JarOutputStream.java
+++ b/luni/src/main/java/java/util/jar/JarOutputStream.java
@@ -37,20 +37,20 @@
*
* @param os
* the {@code OutputStream} to write to
- * @param mf
+ * @param manifest
* the {@code Manifest} to output for this JAR file.
* @throws IOException
* if an error occurs creating the {@code JarOutputStream}.
*/
- public JarOutputStream(OutputStream os, Manifest mf) throws IOException {
+ public JarOutputStream(OutputStream os, Manifest manifest) throws IOException {
super(os);
- if (mf == null) {
- throw new NullPointerException();
+ if (manifest == null) {
+ throw new NullPointerException("manifest == null");
}
- manifest = mf;
+ this.manifest = manifest;
ZipEntry ze = new ZipEntry(JarFile.MANIFEST_NAME);
putNextEntry(ze);
- manifest.write(this);
+ this.manifest.write(this);
closeEntry();
}
diff --git a/luni/src/main/java/java/util/logging/FileHandler.java b/luni/src/main/java/java/util/logging/FileHandler.java
index 980955a..6ffef87 100644
--- a/luni/src/main/java/java/util/logging/FileHandler.java
+++ b/luni/src/main/java/java/util/logging/FileHandler.java
@@ -214,8 +214,10 @@
String className = this.getClass().getName();
pattern = (p == null) ? getStringProperty(className + ".pattern",
DEFAULT_PATTERN) : p;
- if (pattern == null || pattern.isEmpty()) {
- throw new NullPointerException("Pattern cannot be empty or null");
+ if (pattern == null) {
+ throw new NullPointerException("pattern == null");
+ } else if (pattern.isEmpty()) {
+ throw new NullPointerException("pattern.isEmpty()");
}
append = (a == null) ? getBooleanProperty(className + ".append",
DEFAULT_APPEND) : a.booleanValue();
diff --git a/luni/src/main/java/java/util/logging/Handler.java b/luni/src/main/java/java/util/logging/Handler.java
index 5a6c14e..13dbdd5 100644
--- a/luni/src/main/java/java/util/logging/Handler.java
+++ b/luni/src/main/java/java/util/logging/Handler.java
@@ -227,7 +227,7 @@
*/
public boolean isLoggable(LogRecord record) {
if (record == null) {
- throw new NullPointerException();
+ throw new NullPointerException("record == null");
}
if (this.level.intValue() == Level.OFF.intValue()) {
return false;
@@ -294,17 +294,17 @@
/**
* Sets the error manager for this handler.
*
- * @param em
+ * @param newErrorManager
* the error manager to set.
* @throws NullPointerException
* if {@code em} is {@code null}.
*/
- public void setErrorManager(ErrorManager em) {
+ public void setErrorManager(ErrorManager newErrorManager) {
LogManager.getLogManager().checkAccess();
- if (em == null) {
- throw new NullPointerException();
+ if (newErrorManager == null) {
+ throw new NullPointerException("newErrorManager == null");
}
- this.errorMan = em;
+ this.errorMan = newErrorManager;
}
/**
@@ -327,7 +327,7 @@
*/
void internalSetFormatter(Formatter newFormatter) {
if (newFormatter == null) {
- throw new NullPointerException();
+ throw new NullPointerException("newFormatter == null");
}
this.formatter = newFormatter;
}
@@ -356,7 +356,7 @@
*/
public void setLevel(Level newLevel) {
if (newLevel == null) {
- throw new NullPointerException();
+ throw new NullPointerException("newLevel == null");
}
LogManager.getLogManager().checkAccess();
this.level = newLevel;
diff --git a/luni/src/main/java/java/util/logging/LogManager.java b/luni/src/main/java/java/util/logging/LogManager.java
index 449c263..8877cd5 100644
--- a/luni/src/main/java/java/util/logging/LogManager.java
+++ b/luni/src/main/java/java/util/logging/LogManager.java
@@ -446,7 +446,7 @@
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
if (l == null) {
- throw new NullPointerException();
+ throw new NullPointerException("l == null");
}
checkAccess();
listeners.addPropertyChangeListener(l);
diff --git a/luni/src/main/java/java/util/logging/StreamHandler.java b/luni/src/main/java/java/util/logging/StreamHandler.java
index 7581835..60b4321 100644
--- a/luni/src/main/java/java/util/logging/StreamHandler.java
+++ b/luni/src/main/java/java/util/logging/StreamHandler.java
@@ -167,7 +167,7 @@
*/
protected void setOutputStream(OutputStream os) {
if (os == null) {
- throw new NullPointerException();
+ throw new NullPointerException("os == null");
}
LogManager.getLogManager().checkAccess();
close(true);
diff --git a/luni/src/main/java/java/util/prefs/AbstractPreferences.java b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
index d86d789..71110c3 100644
--- a/luni/src/main/java/java/util/prefs/AbstractPreferences.java
+++ b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
@@ -372,7 +372,7 @@
@Override
public void exportNode(OutputStream ostream) throws IOException, BackingStoreException {
if (ostream == null) {
- throw new NullPointerException("Stream is null");
+ throw new NullPointerException("ostream == null");
}
checkState();
XMLParser.exportPrefs(this, ostream, false);
@@ -381,7 +381,7 @@
@Override
public void exportSubtree(OutputStream ostream) throws IOException, BackingStoreException {
if (ostream == null) {
- throw new NullPointerException("Stream is null");
+ throw new NullPointerException("ostream == null");
}
checkState();
XMLParser.exportPrefs(this, ostream, true);
@@ -402,7 +402,7 @@
@Override
public String get(String key, String deflt) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
String result = null;
synchronized (lock) {
@@ -597,7 +597,7 @@
@Override
public boolean nodeExists(String name) throws BackingStoreException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
AbstractPreferences startNode = null;
synchronized (lock) {
@@ -640,8 +640,10 @@
@Override
public void put(String key, String value) {
- if (key == null || value == null) {
- throw new NullPointerException();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (value == null) {
+ throw new NullPointerException("value == null");
}
if (key.length() > MAX_KEY_LENGTH || value.length() > MAX_VALUE_LENGTH) {
throw new IllegalArgumentException();
@@ -730,7 +732,7 @@
@Override
public void addNodeChangeListener(NodeChangeListener ncl) {
if (ncl == null) {
- throw new NullPointerException();
+ throw new NullPointerException("ncl == null");
}
checkState();
synchronized (nodeChangeListeners) {
@@ -741,7 +743,7 @@
@Override
public void addPreferenceChangeListener(PreferenceChangeListener pcl) {
if (pcl == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pcl == null");
}
checkState();
synchronized (preferenceChangeListeners) {
diff --git a/luni/src/main/java/java/util/regex/Matcher.java b/luni/src/main/java/java/util/regex/Matcher.java
index 58bcf9e..cfd4432 100644
--- a/luni/src/main/java/java/util/regex/Matcher.java
+++ b/luni/src/main/java/java/util/regex/Matcher.java
@@ -50,11 +50,6 @@
private int regionEnd;
/**
- * Holds the position where the next find operation will take place.
- */
- private int findPos;
-
- /**
* Holds the position where the next append operation will take place.
*/
private int appendPos;
@@ -212,7 +207,6 @@
resetForInput();
matchFound = false;
- findPos = regionStart;
appendPos = 0;
return this;
@@ -377,30 +371,17 @@
}
/**
- * Returns the next occurrence of the {@link Pattern} in the input. The
- * method starts the search from the given character in the input.
+ * Returns true if there is another match in the input, starting
+ * from the given position. The region is ignored.
*
- * @param start
- * The index in the input at which the find operation is to
- * begin. If this is less than the start of the region, it is
- * automatically adjusted to that value. If it is beyond the end
- * of the region, the method will fail.
- * @return true if (and only if) a match has been found.
+ * @throws IndexOutOfBoundsException if {@code start < 0 || start > input.length()}
*/
public boolean find(int start) {
- findPos = start;
-
- if (findPos < regionStart) {
- findPos = regionStart;
- } else if (findPos >= regionEnd) {
- matchFound = false;
- return false;
+ if (start < 0 || start > input.length()) {
+ throw new IndexOutOfBoundsException("start=" + start + "; length=" + input.length());
}
- matchFound = findImpl(address, input, findPos, matchOffsets);
- if (matchFound) {
- findPos = matchOffsets[1];
- }
+ matchFound = findImpl(address, input, start, matchOffsets);
return matchFound;
}
@@ -414,9 +395,6 @@
*/
public boolean find() {
matchFound = findNextImpl(address, input, matchOffsets);
- if (matchFound) {
- findPos = matchOffsets[1];
- }
return matchFound;
}
@@ -429,9 +407,6 @@
*/
public boolean lookingAt() {
matchFound = lookingAtImpl(address, input, matchOffsets);
- if (matchFound) {
- findPos = matchOffsets[1];
- }
return matchFound;
}
@@ -444,9 +419,6 @@
*/
public boolean matches() {
matchFound = matchesImpl(address, input, matchOffsets);
- if (matchFound) {
- findPos = matchOffsets[1];
- }
return matchFound;
}
diff --git a/luni/src/main/java/java/util/regex/Pattern.java b/luni/src/main/java/java/util/regex/Pattern.java
index 46984b9..cbd5965 100644
--- a/luni/src/main/java/java/util/regex/Pattern.java
+++ b/luni/src/main/java/java/util/regex/Pattern.java
@@ -82,16 +82,23 @@
* </table>
* <p>Most of the time, the built-in character classes are more useful:
* <table>
- * <tr> <td> \d </td> <td>Any digit character.</td> </tr>
- * <tr> <td> \D </td> <td>Any non-digit character.</td> </tr>
- * <tr> <td> \s </td> <td>Any whitespace character.</td> </tr>
- * <tr> <td> \S </td> <td>Any non-whitespace character.</td> </tr>
- * <tr> <td> \w </td> <td>Any word character.</td> </tr>
- * <tr> <td> \W </td> <td>Any non-word character.</td> </tr>
+ * <tr> <td> \d </td> <td>Any digit character (see note below).</td> </tr>
+ * <tr> <td> \D </td> <td>Any non-digit character (see note below).</td> </tr>
+ * <tr> <td> \s </td> <td>Any whitespace character (see note below).</td> </tr>
+ * <tr> <td> \S </td> <td>Any non-whitespace character (see note below).</td> </tr>
+ * <tr> <td> \w </td> <td>Any word character (see note below).</td> </tr>
+ * <tr> <td> \W </td> <td>Any non-word character (see note below).</td> </tr>
* <tr> <td> \p{<i>NAME</i>} </td> <td> Any character in the class with the given <i>NAME</i>. </td> </tr>
* <tr> <td> \P{<i>NAME</i>} </td> <td> Any character <i>not</i> in the named class. </td> </tr>
* </table>
- * <p>There are a variety of named classes:
+ * <p>Note that these built-in classes don't just cover the traditional ASCII range. For example,
+ * <code>\w</code> is equivalent to the character class <code>[\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}]</code>.
+ * For more details see <a href="http://www.unicode.org/reports/tr18/#Compatibility_Properties">Unicode TR-18</a>,
+ * and bear in mind that the set of characters in each class can vary between Unicode releases.
+ * If you actually want to match only ASCII characters, specify the explicit characters you want;
+ * if you mean 0-9 use <code>[0-9]</code> rather than <code>\d</code>, which would also include
+ * Gurmukhi digits and so forth.
+ * <p>There are also a variety of named classes:
* <ul>
* <li><a href="../../lang/Character.html#unicode_categories">Unicode category names</a>,
* prefixed by {@code Is}. For example {@code \p{IsLu}} for all uppercase letters.
diff --git a/luni/src/main/java/java/util/zip/DeflaterInputStream.java b/luni/src/main/java/java/util/zip/DeflaterInputStream.java
index c6e95f2..805ce17 100644
--- a/luni/src/main/java/java/util/zip/DeflaterInputStream.java
+++ b/luni/src/main/java/java/util/zip/DeflaterInputStream.java
@@ -72,8 +72,10 @@
*/
public DeflaterInputStream(InputStream in, Deflater deflater, int bufferSize) {
super(in);
- if (in == null || deflater == null) {
- throw new NullPointerException();
+ if (in == null) {
+ throw new NullPointerException("in == null");
+ } else if (deflater == null) {
+ throw new NullPointerException("deflater == null");
}
if (bufferSize <= 0) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
index b0bcb99..d336e72 100644
--- a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
+++ b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
@@ -115,8 +115,10 @@
*/
public DeflaterOutputStream(OutputStream os, Deflater def, int bsize, boolean syncFlush) {
super(os);
- if (os == null || def == null) {
- throw new NullPointerException();
+ if (os == null) {
+ throw new NullPointerException("os == null");
+ } else if (def == null) {
+ throw new NullPointerException("def == null");
}
if (bsize <= 0) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/util/zip/InflaterInputStream.java b/luni/src/main/java/java/util/zip/InflaterInputStream.java
index 580d605..6081037 100644
--- a/luni/src/main/java/java/util/zip/InflaterInputStream.java
+++ b/luni/src/main/java/java/util/zip/InflaterInputStream.java
@@ -103,8 +103,10 @@
*/
public InflaterInputStream(InputStream is, Inflater inflater, int bsize) {
super(is);
- if (is == null || inflater == null) {
- throw new NullPointerException();
+ if (is == null) {
+ throw new NullPointerException("is == null");
+ } else if (inflater == null) {
+ throw new NullPointerException("inflater == null");
}
if (bsize <= 0) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/util/zip/InflaterOutputStream.java b/luni/src/main/java/java/util/zip/InflaterOutputStream.java
index c9687b6..9a699a8 100644
--- a/luni/src/main/java/java/util/zip/InflaterOutputStream.java
+++ b/luni/src/main/java/java/util/zip/InflaterOutputStream.java
@@ -70,8 +70,10 @@
*/
public InflaterOutputStream(OutputStream out, Inflater inf, int bufferSize) {
super(out);
- if (out == null || inf == null) {
- throw new NullPointerException();
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ } else if (inf == null) {
+ throw new NullPointerException("inf == null");
}
if (bufferSize <= 0) {
throw new IllegalArgumentException();
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java
index b5d5b36..19680df 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -72,7 +72,7 @@
*/
public ZipEntry(String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (name.length() > 0xFFFF) {
throw new IllegalArgumentException("Name too long: " + name.length());
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java
index b538177..a79984c 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -239,7 +239,7 @@
public ZipEntry getEntry(String entryName) {
checkNotClosed();
if (entryName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("entryName == null");
}
ZipEntry ze = mEntries.get(entryName);
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index 788e90b..019e878 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -101,7 +101,7 @@
public ZipInputStream(InputStream stream) {
super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true));
if (stream == null) {
- throw new NullPointerException();
+ throw new NullPointerException("stream == null");
}
}
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java
index 1dacd46..aeb5def 100644
--- a/luni/src/main/java/javax/crypto/Cipher.java
+++ b/luni/src/main/java/javax/crypto/Cipher.java
@@ -141,10 +141,10 @@
protected Cipher(CipherSpi cipherSpi, Provider provider,
String transformation) {
if (cipherSpi == null) {
- throw new NullPointerException();
+ throw new NullPointerException("cipherSpi == null");
}
if (!(cipherSpi instanceof NullCipherSpi) && provider == null) {
- throw new NullPointerException();
+ throw new NullPointerException("provider == null");
}
this.provider = provider;
this.transformation = transformation;
@@ -1332,7 +1332,7 @@
public static final int getMaxAllowedKeyLength(String transformation)
throws NoSuchAlgorithmException {
if (transformation == null) {
- throw new NullPointerException();
+ throw new NullPointerException("transformation == null");
}
checkTransformation(transformation);
//FIXME jurisdiction policy files
@@ -1356,7 +1356,7 @@
public static final AlgorithmParameterSpec getMaxAllowedParameterSpec(
String transformation) throws NoSuchAlgorithmException {
if (transformation == null) {
- throw new NullPointerException();
+ throw new NullPointerException("transformation == null");
}
checkTransformation(transformation);
//FIXME jurisdiction policy files
diff --git a/luni/src/main/java/javax/crypto/CipherInputStream.java b/luni/src/main/java/javax/crypto/CipherInputStream.java
index 39dcfda..a59a425 100644
--- a/luni/src/main/java/javax/crypto/CipherInputStream.java
+++ b/luni/src/main/java/javax/crypto/CipherInputStream.java
@@ -135,7 +135,7 @@
@Override
public int read(byte[] buf, int off, int len) throws IOException {
if (in == null) {
- throw new NullPointerException("Underlying input stream is null");
+ throw new NullPointerException("in == null");
}
int i;
diff --git a/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java b/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
index 034f07a..0fb5b76 100644
--- a/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
+++ b/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
@@ -121,7 +121,7 @@
* Creates an {@code EncryptedPrivateKeyInfo} instance from an algorithm
* name and its encrypted data.
*
- * @param encrAlgName
+ * @param encryptionAlgorithmName
* the name of an algorithm.
* @param encryptedData
* the encrypted data.
@@ -133,12 +133,12 @@
* @throws IllegalArgumentException
* if {@code encryptedData} is empty.
*/
- public EncryptedPrivateKeyInfo(String encrAlgName, byte[] encryptedData)
+ public EncryptedPrivateKeyInfo(String encryptionAlgorithmName, byte[] encryptedData)
throws NoSuchAlgorithmException {
- if (encrAlgName == null) {
- throw new NullPointerException("the algName parameter is null");
+ if (encryptionAlgorithmName == null) {
+ throw new NullPointerException("encryptionAlgorithmName == null");
}
- this.algName = encrAlgName;
+ this.algName = encryptionAlgorithmName;
if (!mapAlgName()) {
throw new NoSuchAlgorithmException("Unsupported algorithm: " + this.algName);
}
diff --git a/luni/src/main/java/javax/crypto/ExemptionMechanism.java b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
index 2ac4994..8745b78 100644
--- a/luni/src/main/java/javax/crypto/ExemptionMechanism.java
+++ b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
@@ -98,7 +98,7 @@
public static final ExemptionMechanism getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new ExemptionMechanism((ExemptionMechanismSpi) sap.spi, sap.provider, algorithm);
@@ -134,7 +134,7 @@
throw new NoSuchProviderException(provider);
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
return getInstance(algorithm, impProvider);
}
@@ -159,7 +159,7 @@
public static final ExemptionMechanism getInstance(String algorithm,
Provider provider) throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
if (provider == null) {
throw new IllegalArgumentException("provider == null");
diff --git a/luni/src/main/java/javax/crypto/KeyAgreement.java b/luni/src/main/java/javax/crypto/KeyAgreement.java
index 9c5d86c..51b4cd1 100644
--- a/luni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/luni/src/main/java/javax/crypto/KeyAgreement.java
@@ -99,7 +99,7 @@
public static final KeyAgreement getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new KeyAgreement((KeyAgreementSpi) sap.spi, sap.provider, algorithm);
@@ -161,7 +161,7 @@
throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new KeyAgreement((KeyAgreementSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/crypto/KeyGenerator.java b/luni/src/main/java/javax/crypto/KeyGenerator.java
index 77b1a82..606998a 100644
--- a/luni/src/main/java/javax/crypto/KeyGenerator.java
+++ b/luni/src/main/java/javax/crypto/KeyGenerator.java
@@ -98,7 +98,7 @@
public static final KeyGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new KeyGenerator((KeyGeneratorSpi) sap.spi, sap.provider, algorithm);
@@ -158,7 +158,7 @@
throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new KeyGenerator((KeyGeneratorSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/crypto/Mac.java b/luni/src/main/java/javax/crypto/Mac.java
index 1a05b59..46be141 100644
--- a/luni/src/main/java/javax/crypto/Mac.java
+++ b/luni/src/main/java/javax/crypto/Mac.java
@@ -101,7 +101,7 @@
public static final Mac getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new Mac((MacSpi) sap.spi, sap.provider, algorithm);
@@ -163,7 +163,7 @@
throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new Mac((MacSpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/crypto/SealedObject.java b/luni/src/main/java/javax/crypto/SealedObject.java
index c9c1534..cfb970b 100644
--- a/luni/src/main/java/javax/crypto/SealedObject.java
+++ b/luni/src/main/java/javax/crypto/SealedObject.java
@@ -33,12 +33,12 @@
/**
* A {@code SealedObject} is a wrapper around a {@code serializable} object
* instance and encrypts it using a cryptographic cipher.
- * <p>
- * Since a {@code SealedObject} instance is a serializable object itself it can
+ *
+ * <p>Since a {@code SealedObject} instance is serializable it can
* either be stored or transmitted over an insecure channel.
- * <p>
- * The wrapped object can later be decrypted (unsealed) using the corresponding
- * key and then be deserialized to retrieve the original object.The sealed
+ *
+ * <p>The wrapped object can later be decrypted (unsealed) using the corresponding
+ * key and then be deserialized to retrieve the original object. The sealed
* object itself keeps track of the cipher and corresponding parameters.
*/
public class SealedObject implements Serializable {
@@ -46,19 +46,25 @@
private static final long serialVersionUID = 4482838265551344752L;
/**
- * The {@link AlgorithmParameters} in encoded format.
+ * The cipher's {@link AlgorithmParameters} in encoded format.
+ * Equivalent to {@code cipher.getParameters().getEncoded()},
+ * or null if the cipher did not use any parameters.
*/
protected byte[] encodedParams;
+
private byte[] encryptedContent;
private String sealAlg;
private String paramsAlg;
- private void readObject(ObjectInputStream s)
- throws IOException, ClassNotFoundException {
- encodedParams = (byte []) s.readUnshared();
- encryptedContent = (byte []) s.readUnshared();
- sealAlg = (String) s.readUnshared();
- paramsAlg = (String) s.readUnshared();
+ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
+ // We do unshared reads here to ensure we have our own clones of the byte[]s.
+ encodedParams = (byte[]) s.readUnshared();
+ encryptedContent = (byte[]) s.readUnshared();
+ // These are regular shared reads because the algorithms used by a given stream are
+ // almost certain to the be same for each object, and String is immutable anyway,
+ // so there's no security concern about sharing.
+ sealAlg = (String) s.readObject();
+ paramsAlg = (String) s.readObject();
}
/**
diff --git a/luni/src/main/java/javax/crypto/SecretKeyFactory.java b/luni/src/main/java/javax/crypto/SecretKeyFactory.java
index 5e47abe1..8ab3eb8 100644
--- a/luni/src/main/java/javax/crypto/SecretKeyFactory.java
+++ b/luni/src/main/java/javax/crypto/SecretKeyFactory.java
@@ -103,7 +103,7 @@
public static final SecretKeyFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new SecretKeyFactory((SecretKeyFactorySpi) sap.spi, sap.provider, algorithm);
@@ -165,7 +165,7 @@
throw new IllegalArgumentException("provider == null");
}
if (algorithm == null) {
- throw new NullPointerException();
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new SecretKeyFactory((SecretKeyFactorySpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java b/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java
index fe86aeb..fcfe749 100644
--- a/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java
+++ b/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java
@@ -45,7 +45,7 @@
*/
public DESedeKeySpec(byte[] key) throws InvalidKeyException {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
if (key.length < DES_EDE_KEY_LEN) {
throw new InvalidKeyException();
@@ -71,7 +71,7 @@
*/
public DESedeKeySpec(byte[] key, int offset) throws InvalidKeyException {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
if (key.length - offset < DES_EDE_KEY_LEN) {
throw new InvalidKeyException();
diff --git a/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java b/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java
index 3bc9ab4..340e57f 100644
--- a/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java
+++ b/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java
@@ -73,8 +73,12 @@
*/
public OAEPParameterSpec(String mdName, String mgfName,
AlgorithmParameterSpec mgfSpec, PSource pSrc) {
- if ((mdName == null) || (mgfName == null) || (pSrc == null)) {
- throw new NullPointerException();
+ if (mdName == null) {
+ throw new NullPointerException("mdName == null");
+ } else if (mgfName == null) {
+ throw new NullPointerException("mgfName == null");
+ } else if (pSrc == null) {
+ throw new NullPointerException("pSrc == null");
}
this.mdName = mdName;
this.mgfName = mgfName;
diff --git a/luni/src/main/java/javax/crypto/spec/PSource.java b/luni/src/main/java/javax/crypto/spec/PSource.java
index 0efa1e9..f644316 100644
--- a/luni/src/main/java/javax/crypto/spec/PSource.java
+++ b/luni/src/main/java/javax/crypto/spec/PSource.java
@@ -40,7 +40,7 @@
*/
protected PSource(String pSrcName) {
if (pSrcName == null) {
- throw new NullPointerException();
+ throw new NullPointerException("pSrcName == null");
}
this.pSrcName = pSrcName;
}
@@ -85,7 +85,7 @@
public PSpecified(byte[] p) {
super("PSpecified");
if (p == null) {
- throw new NullPointerException();
+ throw new NullPointerException("p == null");
}
//TODO: It is unknown which name should be used!
//super("");
diff --git a/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java b/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java
index 82ce8a1..0b3db61 100644
--- a/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java
+++ b/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java
@@ -68,7 +68,7 @@
public static final KeyManagerFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException("algorithm is null");
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new KeyManagerFactory((KeyManagerFactorySpi) sap.spi, sap.provider, algorithm);
@@ -127,7 +127,7 @@
throw new IllegalArgumentException("Provider is null");
}
if (algorithm == null) {
- throw new NullPointerException("algorithm is null");
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new KeyManagerFactory((KeyManagerFactorySpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/net/ssl/SSLContext.java b/luni/src/main/java/javax/net/ssl/SSLContext.java
index 9097bb9..a59f301 100644
--- a/luni/src/main/java/javax/net/ssl/SSLContext.java
+++ b/luni/src/main/java/javax/net/ssl/SSLContext.java
@@ -93,7 +93,7 @@
*/
public static SSLContext getInstance(String protocol) throws NoSuchAlgorithmException {
if (protocol == null) {
- throw new NullPointerException("protocol is null");
+ throw new NullPointerException("protocol == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(protocol, null);
return new SSLContext((SSLContextSpi) sap.spi, sap.provider, protocol);
@@ -154,7 +154,7 @@
throw new IllegalArgumentException("provider is null");
}
if (protocol == null) {
- throw new NullPointerException("protocol is null");
+ throw new NullPointerException("protocol == null");
}
Object spi = ENGINE.getInstance(protocol, provider, null);
return new SSLContext((SSLContextSpi) spi, provider, protocol);
diff --git a/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java b/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java
index bf3bf8c..be9db06 100644
--- a/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java
+++ b/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java
@@ -67,7 +67,7 @@
public static final TrustManagerFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
- throw new NullPointerException("algorithm is null");
+ throw new NullPointerException("algorithm == null");
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
return new TrustManagerFactory((TrustManagerFactorySpi) sap.spi, sap.provider, algorithm);
@@ -126,7 +126,7 @@
throw new IllegalArgumentException("Provider is null");
}
if (algorithm == null) {
- throw new NullPointerException("algorithm is null");
+ throw new NullPointerException("algorithm == null");
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
return new TrustManagerFactory((TrustManagerFactorySpi) spi, provider, algorithm);
diff --git a/luni/src/main/java/javax/security/auth/Subject.java b/luni/src/main/java/javax/security/auth/Subject.java
index a958484..6c9c036 100644
--- a/luni/src/main/java/javax/security/auth/Subject.java
+++ b/luni/src/main/java/javax/security/auth/Subject.java
@@ -116,8 +116,12 @@
public Subject(boolean readOnly, Set<? extends Principal> subjPrincipals,
Set<?> pubCredentials, Set<?> privCredentials) {
- if (subjPrincipals == null || pubCredentials == null || privCredentials == null) {
- throw new NullPointerException();
+ if (subjPrincipals == null) {
+ throw new NullPointerException("subjPrincipals == null");
+ } else if (pubCredentials == null) {
+ throw new NullPointerException("pubCredentials == null");
+ } else if (privCredentials == null) {
+ throw new NullPointerException("privCredentials == null");
}
principals = new SecureSet<Principal>(_PRINCIPALS, subjPrincipals);
@@ -467,7 +471,7 @@
*/
public static Subject getSubject(final AccessControlContext context) {
if (context == null) {
- throw new NullPointerException("AccessControlContext cannot be null");
+ throw new NullPointerException("context == null");
}
PrivilegedAction<DomainCombiner> action = new PrivilegedAction<DomainCombiner>() {
public DomainCombiner run() {
@@ -554,7 +558,7 @@
private void verifyElement(Object o) {
if (o == null) {
- throw new NullPointerException();
+ throw new NullPointerException("o == null");
}
if (permission == _PRINCIPALS && !(Principal.class.isAssignableFrom(o.getClass()))) {
throw new IllegalArgumentException("Element is not instance of java.security.Principal");
@@ -607,7 +611,7 @@
public boolean retainAll(Collection<?> c) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
return super.retainAll(c);
}
@@ -624,7 +628,7 @@
protected final <E> Set<E> get(final Class<E> c) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
AbstractSet<E> s = new AbstractSet<E>() {
@@ -652,7 +656,7 @@
public boolean retainAll(Collection<?> c) {
if (c == null) {
- throw new NullPointerException();
+ throw new NullPointerException("c == null");
}
return super.retainAll(c);
}
diff --git a/luni/src/main/java/javax/security/auth/x500/X500Principal.java b/luni/src/main/java/javax/security/auth/x500/X500Principal.java
index e6453e9..cedebe0 100644
--- a/luni/src/main/java/javax/security/auth/x500/X500Principal.java
+++ b/luni/src/main/java/javax/security/auth/x500/X500Principal.java
@@ -123,7 +123,7 @@
*/
public X500Principal(String name) {
if (name == null) {
- throw new NullPointerException("Name cannot be null");
+ throw new NullPointerException("name == null");
}
try {
dn = new Name(name);
@@ -134,7 +134,7 @@
public X500Principal(String name, Map<String,String> keywordMap){
if (name == null) {
- throw new NullPointerException("Name cannot be null");
+ throw new NullPointerException("name == null");
}
try {
dn = new Name(substituteNameFromMap(name, keywordMap));
diff --git a/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java b/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java
index 6a89dae..68291b6 100644
--- a/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java
+++ b/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java
@@ -326,7 +326,7 @@
*/
public Duration newDurationDayTime(final String lexicalRepresentation) {
if (lexicalRepresentation == null) {
- throw new NullPointerException("The lexical representation cannot be null.");
+ throw new NullPointerException("lexicalRepresentation == null");
}
// The lexical representation must match the pattern [^YM]*(T.*)?
int pos = lexicalRepresentation.indexOf('T');
@@ -539,7 +539,7 @@
*/
public Duration newDurationYearMonth(final String lexicalRepresentation) {
if (lexicalRepresentation == null) {
- throw new NullPointerException("The lexical representation cannot be null.");
+ throw new NullPointerException("lexicalRepresentation == null");
}
// The lexical representation must match the pattern [^DT]*.
int length = lexicalRepresentation.length();
diff --git a/luni/src/main/java/javax/xml/datatype/Duration.java b/luni/src/main/java/javax/xml/datatype/Duration.java
index 8121d36..fcdd4c5 100644
--- a/luni/src/main/java/javax/xml/datatype/Duration.java
+++ b/luni/src/main/java/javax/xml/datatype/Duration.java
@@ -552,11 +552,7 @@
// check data parameter
if (date == null) {
- throw new NullPointerException(
- "Cannot call "
- + this.getClass().getName()
- + "#addTo(Date date) with date == null."
- );
+ throw new NullPointerException("date == null");
}
Calendar cal = new GregorianCalendar();
diff --git a/luni/src/main/java/javax/xml/namespace/QName.java b/luni/src/main/java/javax/xml/namespace/QName.java
index f748b64..a82487f 100644
--- a/luni/src/main/java/javax/xml/namespace/QName.java
+++ b/luni/src/main/java/javax/xml/namespace/QName.java
@@ -22,8 +22,6 @@
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import javax.xml.XMLConstants;
/**
diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactory.java b/luni/src/main/java/javax/xml/validation/SchemaFactory.java
index 23e4798..2018067 100644
--- a/luni/src/main/java/javax/xml/validation/SchemaFactory.java
+++ b/luni/src/main/java/javax/xml/validation/SchemaFactory.java
@@ -203,8 +203,10 @@
*/
public static SchemaFactory newInstance(String schemaLanguage, String factoryClassName,
ClassLoader classLoader) {
- if (schemaLanguage == null || factoryClassName == null) {
- throw new NullPointerException("schemaLanguage == null || factoryClassName == null");
+ if (schemaLanguage == null) {
+ throw new NullPointerException("schemaLanguage == null");
+ } else if (factoryClassName == null) {
+ throw new NullPointerException("factoryClassName == null");
}
if (classLoader == null) {
classLoader = Thread.currentThread().getContextClassLoader();
@@ -265,7 +267,7 @@
public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
if (name == null) {
- throw new NullPointerException("the name parameter is null");
+ throw new NullPointerException("name == null");
}
throw new SAXNotRecognizedException(name);
}
@@ -313,7 +315,7 @@
*/
public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
if (name == null) {
- throw new NullPointerException("the name parameter is null");
+ throw new NullPointerException("name == null");
}
throw new SAXNotRecognizedException(name);
}
@@ -340,7 +342,7 @@
*/
public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
if (name == null) {
- throw new NullPointerException("the name parameter is null");
+ throw new NullPointerException("name == null");
}
throw new SAXNotRecognizedException(name);
}
@@ -371,7 +373,7 @@
*/
public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
if (name == null) {
- throw new NullPointerException("the name parameter is null");
+ throw new NullPointerException("name == null");
}
throw new SAXNotRecognizedException(name);
}
diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
index 3a6cb83..636777c 100644
--- a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
@@ -131,7 +131,9 @@
* If the <tt>schemaLanguage</tt> parameter is null.
*/
public SchemaFactory newFactory(String schemaLanguage) {
- if(schemaLanguage==null) throw new NullPointerException();
+ if (schemaLanguage == null) {
+ throw new NullPointerException("schemaLanguage == null");
+ }
SchemaFactory f = _newFactory(schemaLanguage);
if (debug) {
if (f != null) {
diff --git a/luni/src/main/java/javax/xml/validation/Validator.java b/luni/src/main/java/javax/xml/validation/Validator.java
index b4ee1ca..ea7908a 100644
--- a/luni/src/main/java/javax/xml/validation/Validator.java
+++ b/luni/src/main/java/javax/xml/validation/Validator.java
@@ -339,7 +339,9 @@
* @see #setFeature(String, boolean)
*/
public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null) throw new NullPointerException("the name parameter is null");
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -372,7 +374,9 @@
* @see #getFeature(String)
*/
public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null) throw new NullPointerException("the name parameter is null");
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -400,7 +404,9 @@
* When the name parameter is null.
*/
public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null) throw new NullPointerException("the name parameter is null");
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -431,7 +437,9 @@
* @see #setProperty(String, Object)
*/
public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null) throw new NullPointerException("the name parameter is null");
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
}
diff --git a/luni/src/main/java/javax/xml/validation/ValidatorHandler.java b/luni/src/main/java/javax/xml/validation/ValidatorHandler.java
index 9606193..2b621ff 100644
--- a/luni/src/main/java/javax/xml/validation/ValidatorHandler.java
+++ b/luni/src/main/java/javax/xml/validation/ValidatorHandler.java
@@ -347,8 +347,9 @@
* @see #setFeature(String, boolean)
*/
public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null)
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -381,8 +382,9 @@
* @see #getFeature(String)
*/
public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null)
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -411,8 +413,9 @@
* When the name parameter is null.
*/
public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null)
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
@@ -443,8 +446,9 @@
* @see #setProperty(String, Object)
*/
public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if(name==null)
- throw new NullPointerException();
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
throw new SAXNotRecognizedException(name);
}
}
diff --git a/luni/src/main/java/javax/xml/xpath/XPathException.java b/luni/src/main/java/javax/xml/xpath/XPathException.java
index 8db369b..376d477 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathException.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathException.java
@@ -48,8 +48,8 @@
*/
public XPathException(String message) {
super(message);
- if ( message == null ) {
- throw new NullPointerException ( "message can't be null");
+ if (message == null) {
+ throw new NullPointerException("message == null");
}
this.cause = null;
}
@@ -66,8 +66,8 @@
public XPathException(Throwable cause) {
super(cause == null ? null : cause.toString());
this.cause = cause;
- if ( cause == null ) {
- throw new NullPointerException ( "cause can't be null");
+ if (cause == null) {
+ throw new NullPointerException("cause == null");
}
}
diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactory.java b/luni/src/main/java/javax/xml/xpath/XPathFactory.java
index 8b1c1fa..57f2195 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathFactory.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathFactory.java
@@ -133,9 +133,7 @@
public static final XPathFactory newInstance(final String uri)
throws XPathFactoryConfigurationException {
if (uri == null) {
- throw new NullPointerException(
- "XPathFactory#newInstance(String uri) cannot be called with uri == null"
- );
+ throw new NullPointerException("uri == null");
}
if (uri.length() == 0) {
throw new IllegalArgumentException(
@@ -167,9 +165,7 @@
public static XPathFactory newInstance(String uri, String factoryClassName,
ClassLoader classLoader) throws XPathFactoryConfigurationException {
if (uri == null) {
- throw new NullPointerException(
- "XPathFactory#newInstance(String uri) cannot be called with uri == null"
- );
+ throw new NullPointerException("uri == null");
}
if (uri.length() == 0) {
throw new IllegalArgumentException(
diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
index 652a1d8..0113e7d 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
@@ -126,7 +126,9 @@
* If the parameter is null.
*/
public XPathFactory newFactory(String uri) {
- if(uri==null) throw new NullPointerException();
+ if (uri == null) {
+ throw new NullPointerException("uri == null");
+ }
XPathFactory f = _newFactory(uri);
if (debug) {
if (f != null) {
diff --git a/luni/src/main/java/javax/xml/xpath/package.html b/luni/src/main/java/javax/xml/xpath/package.html
index edeb448..202a29c 100644
--- a/luni/src/main/java/javax/xml/xpath/package.html
+++ b/luni/src/main/java/javax/xml/xpath/package.html
@@ -165,7 +165,7 @@
XPath xpath = XPathFactory.newInstance().newXPath();
String expression = "/widgets/widget";
InputSource inputSource = new InputSource("widgets.xml");
-NodeSet nodes = (NodeSet) xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
+NodeList nodes = (NodeList) xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
</pre>
<h3>XPath Expressions and Types</h3>
diff --git a/luni/src/main/java/libcore/icu/ErrorCode.java b/luni/src/main/java/libcore/icu/ErrorCode.java
deleted file mode 100644
index c093af2..0000000
--- a/luni/src/main/java/libcore/icu/ErrorCode.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
-******************************************************************************
-* Copyright (C) 1996-2005, International Business Machines Corporation and *
-* others. All Rights Reserved. *
-******************************************************************************
-*
-******************************************************************************
-*/
-
-package libcore.icu;
-
-/**
- * Error exception class mapping ICU error codes of the enum UErrorCode
- * @author syn wee quek
-*/
-public final class ErrorCode extends Exception {
- public static boolean isFailure(int error) {
- return error > U_ZERO_ERROR && error < U_ERROR_LIMIT;
- }
-
- public static RuntimeException throwException(int error) {
- if (error <= U_ZERO_ERROR && error >= U_ERROR_LIMIT) {
- return null;
- }
- switch (error) {
- case U_ILLEGAL_ARGUMENT_ERROR:
- return new IllegalArgumentException(ERROR_NAMES[error]);
- case U_INDEX_OUTOFBOUNDS_ERROR:
- case U_BUFFER_OVERFLOW_ERROR:
- return new ArrayIndexOutOfBoundsException(ERROR_NAMES[error]);
- case U_UNSUPPORTED_ERROR:
- return new UnsupportedOperationException(ERROR_NAMES[error]);
- }
- throw new RuntimeException(ERROR_NAMES[error]);
- }
-
- // The errors needed by our CharsetDecoderICU/CharsetEncoderICU.
- public static final int U_ZERO_ERROR = 0;
- private static final int U_ILLEGAL_ARGUMENT_ERROR = 1;
- private static final int U_INDEX_OUTOFBOUNDS_ERROR = 8;
- public static final int U_INVALID_CHAR_FOUND = 10;
- public static final int U_TRUNCATED_CHAR_FOUND = 11;
- public static final int U_ILLEGAL_CHAR_FOUND = 12;
- public static final int U_BUFFER_OVERFLOW_ERROR = 15;
- private static final int U_UNSUPPORTED_ERROR = 16;
- private static final int U_ERROR_LIMIT = 21;
-
- // TODO: this list is incomplete; get these from native code!
- private static final String ERROR_NAMES[] = {
- "U_ZERO_ERROR",
- "U_ILLEGAL_ARGUMENT_ERROR",
- "U_MISSING_RESOURCE_ERROR",
- "U_INVALID_FORMAT_ERROR",
- "U_FILE_ACCESS_ERROR",
- "U_INTERNAL_PROGRAM_ERROR",
- "U_MESSAGE_PARSE_ERROR",
- "U_MEMORY_ALLOCATION_ERROR",
- "U_INDEX_OUTOFBOUNDS_ERROR",
- "U_PARSE_ERROR",
- "U_INVALID_CHAR_FOUND",
- "U_TRUNCATED_CHAR_FOUND",
- "U_ILLEGAL_CHAR_FOUND",
- "U_INVALID_TABLE_FORMAT",
- "U_INVALID_TABLE_FILE",
- "U_BUFFER_OVERFLOW_ERROR",
- "U_UNSUPPORTED_ERROR",
- "U_RESOURCE_TYPE_MISMATCH",
- "U_ILLEGAL_ESCAPE_SEQUENCE",
- "U_UNSUPPORTED_ESCAPE_SEQUENCE"
- };
-}
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index 7f3303e..9984414 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -142,6 +142,19 @@
public static native String toLowerCase(String s, String localeName);
public static native String toUpperCase(String s, String localeName);
+ // --- Errors.
+
+ // Just the subset of error codes needed by CharsetDecoderICU/CharsetEncoderICU.
+ public static final int U_ZERO_ERROR = 0;
+ public static final int U_INVALID_CHAR_FOUND = 10;
+ public static final int U_TRUNCATED_CHAR_FOUND = 11;
+ public static final int U_ILLEGAL_CHAR_FOUND = 12;
+ public static final int U_BUFFER_OVERFLOW_ERROR = 15;
+
+ public static boolean U_FAILURE(int error) {
+ return error > U_ZERO_ERROR;
+ }
+
// --- Native methods accessing ICU's database.
private static native String[] getAvailableBreakIteratorLocalesNative();
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index cb9c880..8ec2294 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -20,6 +20,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
+import libcore.util.Objects;
/**
* Passes locale-specific from ICU native code to Java.
@@ -46,18 +47,27 @@
public Integer minimalDaysInFirstWeek;
// Used by DateFormatSymbols.
- public String[] amPm;
- public String[] eras;
+ public String[] amPm; // "AM", "PM".
+ public String[] eras; // "BC", "AD".
- public String[] longMonthNames;
- public String[] shortMonthNames;
- public String[] longStandAloneMonthNames;
- public String[] shortStandAloneMonthNames;
+ public String[] longMonthNames; // "January", ...
+ public String[] shortMonthNames; // "Jan", ...
+ public String[] tinyMonthNames; // "J", ...
+ public String[] longStandAloneMonthNames; // "January", ...
+ public String[] shortStandAloneMonthNames; // "Jan", ...
+ public String[] tinyStandAloneMonthNames; // "J", ...
- public String[] longWeekdayNames;
- public String[] shortWeekdayNames;
- public String[] longStandAloneWeekdayNames;
- public String[] shortStandAloneWeekdayNames;
+ public String[] longWeekdayNames; // "Sunday", ...
+ public String[] shortWeekdayNames; // "Sun", ...
+ public String[] tinyWeekdayNames; // "S", ...
+ public String[] longStandAloneWeekdayNames; // "Sunday", ...
+ public String[] shortStandAloneWeekdayNames; // "Sun", ...
+ public String[] tinyStandAloneWeekdayNames; // "S", ...
+
+ // Used by frameworks/base DateSorter and DateUtils.
+ public String yesterday; // "Yesterday".
+ public String today; // "Today".
+ public String tomorrow; // "Tomorrow".
public String fullTimeFormat;
public String longTimeFormat;
@@ -120,44 +130,7 @@
}
@Override public String toString() {
- return "LocaleData[" +
- "firstDayOfWeek=" + firstDayOfWeek + "," +
- "minimalDaysInFirstWeek=" + minimalDaysInFirstWeek + "," +
- "amPm=" + Arrays.toString(amPm) + "," +
- "eras=" + Arrays.toString(eras) + "," +
- "longMonthNames=" + Arrays.toString(longMonthNames) + "," +
- "shortMonthNames=" + Arrays.toString(shortMonthNames) + "," +
- "longStandAloneMonthNames=" + Arrays.toString(longStandAloneMonthNames) + "," +
- "shortStandAloneMonthNames=" + Arrays.toString(shortStandAloneMonthNames) + "," +
- "longWeekdayNames=" + Arrays.toString(longWeekdayNames) + "," +
- "shortWeekdayNames=" + Arrays.toString(shortWeekdayNames) + "," +
- "longStandAloneWeekdayNames=" + Arrays.toString(longStandAloneWeekdayNames) + "," +
- "shortStandAloneWeekdayNames=" + Arrays.toString(shortStandAloneWeekdayNames) + "," +
- "fullTimeFormat=" + fullTimeFormat + "," +
- "longTimeFormat=" + longTimeFormat + "," +
- "mediumTimeFormat=" + mediumTimeFormat + "," +
- "shortTimeFormat=" + shortTimeFormat + "," +
- "fullDateFormat=" + fullDateFormat + "," +
- "longDateFormat=" + longDateFormat + "," +
- "mediumDateFormat=" + mediumDateFormat + "," +
- "shortDateFormat=" + shortDateFormat + "," +
- "zeroDigit=" + zeroDigit + "," +
- "decimalSeparator=" + decimalSeparator + "," +
- "groupingSeparator=" + groupingSeparator + "," +
- "patternSeparator=" + patternSeparator + "," +
- "percent=" + percent + "," +
- "perMill=" + perMill + "," +
- "monetarySeparator=" + monetarySeparator + "," +
- "minusSign=" + minusSign + "," +
- "exponentSeparator=" + exponentSeparator + "," +
- "infinity=" + infinity + "," +
- "NaN=" + NaN + "," +
- "currencySymbol=" + currencySymbol + "," +
- "internationalCurrencySymbol=" + internationalCurrencySymbol + "," +
- "numberPattern=" + numberPattern + "," +
- "integerPattern=" + integerPattern + "," +
- "currencyPattern=" + currencyPattern + "," +
- "percentPattern=" + percentPattern + "]";
+ return Objects.toString(this);
}
public String getDateFormat(int style) {
diff --git a/luni/src/main/java/libcore/icu/NativeConverter.java b/luni/src/main/java/libcore/icu/NativeConverter.java
index 2d8630c..e18f483 100644
--- a/luni/src/main/java/libcore/icu/NativeConverter.java
+++ b/luni/src/main/java/libcore/icu/NativeConverter.java
@@ -54,19 +54,19 @@
}
}
- public static int setCallbackDecode(long converterHandle, CharsetDecoder decoder) {
- return setCallbackDecode(converterHandle,
- translateCodingErrorAction(decoder.malformedInputAction()),
- translateCodingErrorAction(decoder.unmappableCharacterAction()),
- decoder.replacement());
+ public static void setCallbackDecode(long converterHandle, CharsetDecoder decoder) {
+ setCallbackDecode(converterHandle,
+ translateCodingErrorAction(decoder.malformedInputAction()),
+ translateCodingErrorAction(decoder.unmappableCharacterAction()),
+ decoder.replacement());
}
- private static native int setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, String subChars);
+ private static native void setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, String subChars);
- public static int setCallbackEncode(long converterHandle, CharsetEncoder encoder) {
- return setCallbackEncode(converterHandle,
- translateCodingErrorAction(encoder.malformedInputAction()),
- translateCodingErrorAction(encoder.unmappableCharacterAction()),
- encoder.replacement());
+ public static void setCallbackEncode(long converterHandle, CharsetEncoder encoder) {
+ setCallbackEncode(converterHandle,
+ translateCodingErrorAction(encoder.malformedInputAction()),
+ translateCodingErrorAction(encoder.unmappableCharacterAction()),
+ encoder.replacement());
}
- private static native int setCallbackEncode(long converterHandle, int onMalformedInput, int onUnmappableInput, byte[] subBytes);
+ private static native void setCallbackEncode(long converterHandle, int onMalformedInput, int onUnmappableInput, byte[] subBytes);
}
diff --git a/luni/src/main/java/libcore/icu/NativeDecimalFormat.java b/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
index 74775e5..f6b5214 100644
--- a/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
+++ b/luni/src/main/java/libcore/icu/NativeDecimalFormat.java
@@ -29,7 +29,7 @@
import java.util.Currency;
import java.util.NoSuchElementException;
-public final class NativeDecimalFormat {
+public final class NativeDecimalFormat implements Cloneable {
/**
* Constants corresponding to the native type UNumberFormatSymbol, for setSymbol.
*/
@@ -144,22 +144,6 @@
this.lastPattern = pattern;
}
- // Used to implement clone.
- private NativeDecimalFormat(NativeDecimalFormat other) {
- this.address = cloneImpl(other.address);
- this.lastPattern = other.lastPattern;
- this.negPrefNull = other.negPrefNull;
- this.negSuffNull = other.negSuffNull;
- this.posPrefNull = other.posPrefNull;
- this.posSuffNull = other.posSuffNull;
- }
-
- // TODO: remove this and just have DecimalFormat.hashCode do the right thing itself.
- @Override
- public int hashCode() {
- return this.getPositivePrefix().hashCode();
- }
-
public synchronized void close() {
if (address != 0) {
close(address);
@@ -167,9 +151,27 @@
}
}
- @Override
- public Object clone() {
- return new NativeDecimalFormat(this);
+ @Override protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override public Object clone() {
+ try {
+ NativeDecimalFormat clone = (NativeDecimalFormat) super.clone();
+ clone.address = cloneImpl(address);
+ clone.lastPattern = lastPattern;
+ clone.negPrefNull = negPrefNull;
+ clone.negSuffNull = negSuffNull;
+ clone.posPrefNull = posPrefNull;
+ clone.posSuffNull = posSuffNull;
+ return clone;
+ } catch (CloneNotSupportedException unexpected) {
+ throw new AssertionError(unexpected);
+ }
}
/**
diff --git a/luni/src/main/java/libcore/icu/NativeIDN.java b/luni/src/main/java/libcore/icu/NativeIDN.java
index 9bf5cb1..db93379 100644
--- a/luni/src/main/java/libcore/icu/NativeIDN.java
+++ b/luni/src/main/java/libcore/icu/NativeIDN.java
@@ -34,7 +34,7 @@
private static String convert(String s, int flags, boolean toAscii) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
return convertImpl(s, flags, toAscii);
}
diff --git a/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java b/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java
index d036c98..4221fe6 100644
--- a/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java
+++ b/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java
@@ -46,7 +46,7 @@
public RuleBasedCollatorICU(String rules) throws ParseException {
if (rules == null) {
- throw new NullPointerException();
+ throw new NullPointerException("rules == null");
}
address = NativeCollation.openCollatorFromRules(rules, VALUE_OFF, VALUE_DEFAULT_STRENGTH);
}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 4f2858d..61c9765 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -50,7 +50,7 @@
}
}
- @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException {
+ @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return tagSocket(os.accept(fd, peerAddress));
}
@@ -80,7 +80,7 @@
return linger.isOn() && linger.l_linger > 0;
}
- @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException {
+ @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
os.connect(fd, address, port);
}
@@ -154,22 +154,22 @@
return os.readv(fd, buffers, offsets, byteCounts);
}
- @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return os.recvfrom(fd, buffer, flags, srcAddress);
}
- @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
}
- @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
BlockGuard.getThreadPolicy().onNetwork();
return os.sendto(fd, buffer, flags, inetAddress, port);
}
- @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
// We permit datagrams without hostname lookups.
if (inetAddress != null) {
BlockGuard.getThreadPolicy().onNetwork();
diff --git a/luni/src/main/java/libcore/io/DiskLruCache.java b/luni/src/main/java/libcore/io/DiskLruCache.java
index 4699766..8338983 100644
--- a/luni/src/main/java/libcore/io/DiskLruCache.java
+++ b/luni/src/main/java/libcore/io/DiskLruCache.java
@@ -16,7 +16,6 @@
package libcore.io;
-import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.EOFException;
@@ -227,13 +226,14 @@
}
private void readJournal() throws IOException {
- InputStream in = new BufferedInputStream(new FileInputStream(journalFile));
+ StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile),
+ Charsets.US_ASCII);
try {
- String magic = Streams.readAsciiLine(in);
- String version = Streams.readAsciiLine(in);
- String appVersionString = Streams.readAsciiLine(in);
- String valueCountString = Streams.readAsciiLine(in);
- String blank = Streams.readAsciiLine(in);
+ String magic = reader.readLine();
+ String version = reader.readLine();
+ String appVersionString = reader.readLine();
+ String valueCountString = reader.readLine();
+ String blank = reader.readLine();
if (!MAGIC.equals(magic)
|| !VERSION_1.equals(version)
|| !Integer.toString(appVersion).equals(appVersionString)
@@ -245,13 +245,13 @@
while (true) {
try {
- readJournalLine(Streams.readAsciiLine(in));
+ readJournalLine(reader.readLine());
} catch (EOFException endOfJournal) {
break;
}
}
} finally {
- IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(reader);
}
}
@@ -458,9 +458,14 @@
// if this edit is creating the entry for the first time, every index must have a value
if (success && !entry.readable) {
for (int i = 0; i < valueCount; i++) {
+ if (!editor.written[i]) {
+ editor.abort();
+ throw new IllegalStateException("Newly created entry didn't create value for index " + i);
+ }
if (!entry.getDirtyFile(i).exists()) {
editor.abort();
- throw new IllegalStateException("edit didn't create file " + i);
+ System.logW("DiskLruCache: Newly created entry doesn't have file for index " + i);
+ return;
}
}
}
@@ -659,10 +664,12 @@
*/
public final class Editor {
private final Entry entry;
+ private final boolean[] written;
private boolean hasErrors;
private Editor(Entry entry) {
this.entry = entry;
+ this.written = (entry.readable) ? null : new boolean[valueCount];
}
/**
@@ -702,6 +709,9 @@
if (entry.currentEditor != this) {
throw new IllegalStateException();
}
+ if (!entry.readable) {
+ written[index] = true;
+ }
return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
}
}
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 2c8e562..719c5f0 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -20,6 +20,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.net.SocketException;
import java.nio.ByteBuffer;
import libcore.util.MutableInt;
import libcore.util.MutableLong;
@@ -34,15 +35,18 @@
this.os = os;
}
- public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException { return os.accept(fd, peerAddress); }
+ public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); }
public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
- public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.bind(fd, address, port); }
+ public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); }
public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
+ public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); }
public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); }
- public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.connect(fd, address, port); }
+ public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.connect(fd, address, port); }
public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException { return os.dup(oldFd); }
public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException { return os.dup2(oldFd, newFd); }
public String[] environ() { return os.environ(); }
+ public void fchmod(FileDescriptor fd, int mode) throws ErrnoException { os.fchmod(fd, mode); }
+ public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { os.fchown(fd, uid, gid); }
public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); }
public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return os.fcntlLong(fd, cmd, arg); }
public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException { return os.fcntlFlock(fd, cmd, arg); }
@@ -75,6 +79,7 @@
public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException { return os.ioctlInt(fd, cmd, arg); }
public boolean isatty(FileDescriptor fd) { return os.isatty(fd); }
public void kill(int pid, int signal) throws ErrnoException { os.kill(pid, signal); }
+ public void lchown(String path, int uid, int gid) throws ErrnoException { os.lchown(path, uid, gid); }
public void listen(FileDescriptor fd, int backlog) throws ErrnoException { os.listen(fd, backlog); }
public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return os.lseek(fd, offset, whence); }
public StructStat lstat(String path) throws ErrnoException { return os.lstat(path); }
@@ -95,16 +100,17 @@
public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.read(fd, buffer); }
public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.read(fd, bytes, byteOffset, byteCount); }
public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.readv(fd, buffers, offsets, byteCounts); }
- public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, buffer, flags, srcAddress); }
- public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
public void remove(String path) throws ErrnoException { os.remove(path); }
public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, inOffset, byteCount); }
- public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException { return os.sendto(fd, buffer, flags, inetAddress, port); }
- public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); }
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
public void setegid(int egid) throws ErrnoException { os.setegid(egid); }
public void seteuid(int euid) throws ErrnoException { os.seteuid(euid); }
public void setgid(int gid) throws ErrnoException { os.setgid(gid); }
+ public int setsid() throws ErrnoException { return os.setsid(); }
public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptByte(fd, level, option, value); }
public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { os.setsockoptIfreq(fd, level, option, value); }
public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptInt(fd, level, option, value); }
@@ -120,6 +126,8 @@
public String strerror(int errno) { return os.strerror(errno); }
public void symlink(String oldPath, String newPath) throws ErrnoException { os.symlink(oldPath, newPath); }
public long sysconf(int name) { return os.sysconf(name); }
+ public void tcdrain(FileDescriptor fd) throws ErrnoException { os.tcdrain(fd); }
+ public int umask(int mask) { return os.umask(mask); }
public StructUtsname uname() { return os.uname(); }
public int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return os.waitpid(pid, status, options); }
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.write(fd, buffer); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index d637b67..55588d8 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -20,20 +20,24 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.net.SocketException;
import java.nio.ByteBuffer;
import libcore.util.MutableInt;
import libcore.util.MutableLong;
public interface Os {
- public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
+ public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
public boolean access(String path, int mode) throws ErrnoException;
- public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public void chmod(String path, int mode) throws ErrnoException;
+ public void chown(String path, int uid, int gid) throws ErrnoException;
public void close(FileDescriptor fd) throws ErrnoException;
- public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
public String[] environ();
+ public void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
+ public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException;
@@ -67,6 +71,7 @@
public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException;
public boolean isatty(FileDescriptor fd);
public void kill(int pid, int signal) throws ErrnoException;
+ public void lchown(String path, int uid, int gid) throws ErrnoException;
public void listen(FileDescriptor fd, int backlog) throws ErrnoException;
public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException;
public StructStat lstat(String path) throws ErrnoException;
@@ -88,16 +93,17 @@
public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
- public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException;
- public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException;
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public void remove(String path) throws ErrnoException;
public void rename(String oldPath, String newPath) throws ErrnoException;
- public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException;
- public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException;
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
public void setegid(int egid) throws ErrnoException;
public void seteuid(int euid) throws ErrnoException;
public void setgid(int gid) throws ErrnoException;
+ public int setsid() throws ErrnoException;
public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
@@ -114,6 +120,8 @@
public String strerror(int errno);
public void symlink(String oldPath, String newPath) throws ErrnoException;
public long sysconf(int name);
+ public void tcdrain(FileDescriptor fd) throws ErrnoException;
+ public int umask(int mask);
public StructUtsname uname();
public int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/OsConstants.java b/luni/src/main/java/libcore/io/OsConstants.java
index 68a165c..9f381f5 100644
--- a/luni/src/main/java/libcore/io/OsConstants.java
+++ b/luni/src/main/java/libcore/io/OsConstants.java
@@ -224,6 +224,7 @@
public static final int O_CREAT = placeholder();
public static final int O_EXCL = placeholder();
public static final int O_NOCTTY = placeholder();
+ public static final int O_NOFOLLOW = placeholder();
public static final int O_NONBLOCK = placeholder();
public static final int O_RDONLY = placeholder();
public static final int O_RDWR = placeholder();
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 7bbf49f..ad01bcf 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -20,6 +20,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.NioUtils;
import libcore.util.MutableInt;
@@ -28,15 +29,18 @@
public final class Posix implements Os {
Posix() { }
- public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
+ public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
public native boolean access(String path, int mode) throws ErrnoException;
- public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public native void chmod(String path, int mode) throws ErrnoException;
+ public native void chown(String path, int uid, int gid) throws ErrnoException;
public native void close(FileDescriptor fd) throws ErrnoException;
- public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
public native FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
public native String[] environ();
+ public native void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
+ public native void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
public native int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
public native int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException;
@@ -69,6 +73,7 @@
public native int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException;
public native boolean isatty(FileDescriptor fd);
public native void kill(int pid, int signal) throws ErrnoException;
+ public native void lchown(String path, int uid, int gid) throws ErrnoException;
public native void listen(FileDescriptor fd, int backlog) throws ErrnoException;
public native long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException;
public native StructStat lstat(String path) throws ErrnoException;
@@ -119,36 +124,37 @@
}
private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
- public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
if (buffer.isDirect()) {
return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress);
} else {
return recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, srcAddress);
}
}
- public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress);
}
- private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException;
+ private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public native void remove(String path) throws ErrnoException;
public native void rename(String oldPath, String newPath) throws ErrnoException;
public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
- public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
if (buffer.isDirect()) {
return sendtoBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, inetAddress, port);
} else {
return sendtoBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, inetAddress, port);
}
}
- public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
}
- private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException;
+ private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
public native void setegid(int egid) throws ErrnoException;
public native void seteuid(int euid) throws ErrnoException;
public native void setgid(int gid) throws ErrnoException;
+ public native int setsid() throws ErrnoException;
public native void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public native void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
public native void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
@@ -164,6 +170,8 @@
public native String strerror(int errno);
public native void symlink(String oldPath, String newPath) throws ErrnoException;
public native long sysconf(int name);
+ public native void tcdrain(FileDescriptor fd) throws ErrnoException;
+ public native int umask(int mask);
public native StructUtsname uname();
public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
diff --git a/luni/src/main/java/libcore/io/StrictLineReader.java b/luni/src/main/java/libcore/io/StrictLineReader.java
new file mode 100644
index 0000000..36556a0
--- /dev/null
+++ b/luni/src/main/java/libcore/io/StrictLineReader.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.Charsets;
+
+/**
+ * Buffers input from an {@link InputStream} for reading lines.
+ *
+ * This class is used for buffered reading of lines. For purposes of this class, a line ends with
+ * "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated line at
+ * end of input is invalid and will be ignored, the caller may use {@code hasUnterminatedLine()}
+ * to detect it after catching the {@code EOFException}.
+ *
+ * This class is intended for reading input that strictly consists of lines, such as line-based
+ * cache entries or cache journal. Unlike the {@link BufferedReader} which in conjunction with
+ * {@link InputStreamReader} provides similar functionality, this class uses different
+ * end-of-input reporting and a more restrictive definition of a line.
+ *
+ * This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
+ * and 10, respectively, and the representation of no other character contains these values.
+ * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
+ * The default charset is US_ASCII.
+ */
+public class StrictLineReader implements Closeable {
+ private static final byte CR = (byte)'\r';
+ private static final byte LF = (byte)'\n';
+
+ private final InputStream in;
+ private final Charset charset;
+
+ /*
+ * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
+ * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
+ * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
+ * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
+ */
+ private byte[] buf;
+ private int pos;
+ private int end;
+
+ /**
+ * Constructs a new {@code StrictLineReader} with the default capacity and charset.
+ *
+ * @param in the {@code InputStream} to read data from.
+ * @throws NullPointerException if {@code in} is null.
+ */
+ public StrictLineReader(InputStream in) {
+ this(in, 8192);
+ }
+
+ /**
+ * Constructs a new {@code LineReader} with the specified capacity and the default charset.
+ *
+ * @param in the {@code InputStream} to read data from.
+ * @param capacity the capacity of the buffer.
+ * @throws NullPointerException if {@code in} is null.
+ * @throws IllegalArgumentException for negative or zero {@code capacity}.
+ */
+ public StrictLineReader(InputStream in, int capacity) {
+ this(in, capacity, Charsets.US_ASCII);
+ }
+
+ /**
+ * Constructs a new {@code LineReader} with the specified charset and the default capacity.
+ *
+ * @param in the {@code InputStream} to read data from.
+ * @param charset the charset used to decode data.
+ * Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
+ * @throws NullPointerException if {@code in} or {@code charset} is null.
+ * @throws IllegalArgumentException if the specified charset is not supported.
+ */
+ public StrictLineReader(InputStream in, Charset charset) {
+ this(in, 8192, charset);
+ }
+
+ /**
+ * Constructs a new {@code LineReader} with the specified capacity and charset.
+ *
+ * @param in the {@code InputStream} to read data from.
+ * @param capacity the capacity of the buffer.
+ * @param charset the charset used to decode data.
+ * Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
+ * @throws NullPointerException if {@code in} or {@code charset} is null.
+ * @throws IllegalArgumentException if {@code capacity} is negative or zero
+ * or the specified charset is not supported.
+ */
+ public StrictLineReader(InputStream in, int capacity, Charset charset) {
+ if (in == null) {
+ throw new NullPointerException("in == null");
+ } else if (charset == null) {
+ throw new NullPointerException("charset == null");
+ }
+ if (capacity < 0) {
+ throw new IllegalArgumentException("capacity <= 0");
+ }
+ if (!(charset.equals(Charsets.US_ASCII) || charset.equals(Charsets.UTF_8) ||
+ charset.equals(Charsets.ISO_8859_1))) {
+ throw new IllegalArgumentException("Unsupported encoding");
+ }
+
+ this.in = in;
+ this.charset = charset;
+ buf = new byte[capacity];
+ }
+
+ /**
+ * Closes the reader by closing the underlying {@code InputStream} and
+ * marking this reader as closed.
+ *
+ * @throws IOException for errors when closing the underlying {@code InputStream}.
+ */
+ @Override
+ public void close() throws IOException {
+ synchronized (in) {
+ if (buf != null) {
+ buf = null;
+ in.close();
+ }
+ }
+ }
+
+ /**
+ * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
+ * this end of line marker is not included in the result.
+ *
+ * @return the next line from the input.
+ * @throws IOException for underlying {@code InputStream} errors.
+ * @throws EOFException for the end of source stream.
+ */
+ public String readLine() throws IOException {
+ synchronized (in) {
+ if (buf == null) {
+ throw new IOException("LineReader is closed");
+ }
+
+ // Read more data if we are at the end of the buffered data.
+ // Though it's an error to read after an exception, we will let {@code fillBuf()}
+ // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
+ if (pos >= end) {
+ fillBuf();
+ }
+ // Try to find LF in the buffered data and return the line if successful.
+ for (int i = pos; i != end; ++i) {
+ if (buf[i] == LF) {
+ int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
+ String res = new String(buf, pos, lineEnd - pos, charset);
+ pos = i + 1;
+ return res;
+ }
+ }
+
+ // Let's anticipate up to 80 characters on top of those already read.
+ ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
+ @Override
+ public String toString() {
+ int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
+ return new String(buf, 0, length, charset);
+ }
+ };
+
+ while (true) {
+ out.write(buf, pos, end - pos);
+ // Mark unterminated line in case fillBuf throws EOFException or IOException.
+ end = -1;
+ fillBuf();
+ // Try to find LF in the buffered data and return the line if successful.
+ for (int i = pos; i != end; ++i) {
+ if (buf[i] == LF) {
+ if (i != pos) {
+ out.write(buf, pos, i - pos);
+ }
+ pos = i + 1;
+ return out.toString();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Read an {@code int} from a line containing its decimal representation.
+ *
+ * @return the value of the {@code int} from the next line.
+ * @throws IOException for underlying {@code InputStream} errors or conversion error.
+ * @throws EOFException for the end of source stream.
+ */
+ public int readInt() throws IOException {
+ String intString = readLine();
+ try {
+ return Integer.parseInt(intString);
+ } catch (NumberFormatException e) {
+ throw new IOException("expected an int but was \"" + intString + "\"");
+ }
+ }
+
+ /**
+ * Check whether there was an unterminated line at end of input after the line reader reported
+ * end-of-input with EOFException. The value is meaningless in any other situation.
+ *
+ * @return true if there was an unterminated line at end of input.
+ */
+ public boolean hasUnterminatedLine() {
+ return end == -1;
+ }
+
+ /**
+ * Reads new input data into the buffer. Call only with pos == end or end == -1,
+ * depending on the desired outcome if the function throws.
+ *
+ * @throws IOException for underlying {@code InputStream} errors.
+ * @throws EOFException for the end of source stream.
+ */
+ private void fillBuf() throws IOException {
+ int result = in.read(buf, 0, buf.length);
+ if (result == -1) {
+ throw new EOFException();
+ }
+ pos = 0;
+ end = result;
+ }
+}
+
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index f8038f0..6ea0baf 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -350,6 +350,7 @@
add("video/x-ms-wvx", "wvx");
add("video/x-msvideo", "avi");
add("video/x-sgi-movie", "movie");
+ add("video/x-webex", "wrf");
add("x-conference/x-cooltalk", "ice");
add("x-epoc/x-sisx-app", "sisx");
applyOverrides();
diff --git a/luni/src/main/java/libcore/net/UriCodec.java b/luni/src/main/java/libcore/net/UriCodec.java
index bde922b..6624474 100644
--- a/luni/src/main/java/libcore/net/UriCodec.java
+++ b/luni/src/main/java/libcore/net/UriCodec.java
@@ -94,7 +94,7 @@
private void appendEncoded(StringBuilder builder, String s, Charset charset,
boolean isPartiallyEncoded) {
if (s == null) {
- throw new NullPointerException();
+ throw new NullPointerException("s == null");
}
int escapeStart = -1;
diff --git a/luni/src/main/java/libcore/net/http/HttpEngine.java b/luni/src/main/java/libcore/net/http/HttpEngine.java
index 3e4b9d3..a370956 100644
--- a/luni/src/main/java/libcore/net/http/HttpEngine.java
+++ b/luni/src/main/java/libcore/net/http/HttpEngine.java
@@ -69,10 +69,10 @@
* required, use {@link #automaticallyReleaseConnectionToPool()}.
*/
public class HttpEngine {
- private static final CacheResponse BAD_GATEWAY_RESPONSE = new CacheResponse() {
+ private static final CacheResponse GATEWAY_TIMEOUT_RESPONSE = new CacheResponse() {
@Override public Map<String, List<String>> getHeaders() throws IOException {
Map<String, List<String>> result = new HashMap<String, List<String>>();
- result.put(null, Collections.singletonList("HTTP/1.1 502 Bad Gateway"));
+ result.put(null, Collections.singletonList("HTTP/1.1 504 Gateway Timeout"));
return result;
}
@Override public InputStream getBody() throws IOException {
@@ -223,14 +223,15 @@
/*
* The raw response source may require the network, but the request
* headers may forbid network use. In that case, dispose of the network
- * response and use a BAD_GATEWAY response instead.
+ * response and use a GATEWAY_TIMEOUT response instead, as specified
+ * by http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4.
*/
if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) {
if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
IoUtils.closeQuietly(cachedResponseBody);
}
this.responseSource = ResponseSource.CACHE;
- this.cacheResponse = BAD_GATEWAY_RESPONSE;
+ this.cacheResponse = GATEWAY_TIMEOUT_RESPONSE;
RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders());
setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody());
}
diff --git a/luni/src/main/java/libcore/net/http/HttpResponseCache.java b/luni/src/main/java/libcore/net/http/HttpResponseCache.java
index 7b95e1c..1a9dfd1 100644
--- a/luni/src/main/java/libcore/net/http/HttpResponseCache.java
+++ b/luni/src/main/java/libcore/net/http/HttpResponseCache.java
@@ -16,7 +16,6 @@
package libcore.net.http;
-import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -53,7 +52,7 @@
import libcore.io.Base64;
import libcore.io.DiskLruCache;
import libcore.io.IoUtils;
-import libcore.io.Streams;
+import libcore.io.StrictLineReader;
/**
* Cache responses in a directory on the file system. Most clients should use
@@ -100,7 +99,7 @@
if (snapshot == null) {
return null;
}
- entry = new Entry(new BufferedInputStream(snapshot.getInputStream(ENTRY_METADATA)));
+ entry = new Entry(snapshot.getInputStream(ENTRY_METADATA));
} catch (IOException e) {
// Give up because the cache cannot be read.
return null;
@@ -282,6 +281,13 @@
super.close();
editor.commit();
}
+
+ @Override
+ public void write(byte[] buffer, int offset, int length) throws IOException {
+ // Since we don't override "write(int oneByte)", we can write directly to "out"
+ // and avoid the inefficient implementation from the FilterOutputStream.
+ out.write(buffer, offset, length);
+ }
};
}
@@ -362,29 +368,30 @@
*/
public Entry(InputStream in) throws IOException {
try {
- uri = Streams.readAsciiLine(in);
- requestMethod = Streams.readAsciiLine(in);
+ StrictLineReader reader = new StrictLineReader(in, Charsets.US_ASCII);
+ uri = reader.readLine();
+ requestMethod = reader.readLine();
varyHeaders = new RawHeaders();
- int varyRequestHeaderLineCount = readInt(in);
+ int varyRequestHeaderLineCount = reader.readInt();
for (int i = 0; i < varyRequestHeaderLineCount; i++) {
- varyHeaders.addLine(Streams.readAsciiLine(in));
+ varyHeaders.addLine(reader.readLine());
}
responseHeaders = new RawHeaders();
- responseHeaders.setStatusLine(Streams.readAsciiLine(in));
- int responseHeaderLineCount = readInt(in);
+ responseHeaders.setStatusLine(reader.readLine());
+ int responseHeaderLineCount = reader.readInt();
for (int i = 0; i < responseHeaderLineCount; i++) {
- responseHeaders.addLine(Streams.readAsciiLine(in));
+ responseHeaders.addLine(reader.readLine());
}
if (isHttps()) {
- String blank = Streams.readAsciiLine(in);
+ String blank = reader.readLine();
if (!blank.isEmpty()) {
throw new IOException("expected \"\" but was \"" + blank + "\"");
}
- cipherSuite = Streams.readAsciiLine(in);
- peerCertificates = readCertArray(in);
- localCertificates = readCertArray(in);
+ cipherSuite = reader.readLine();
+ peerCertificates = readCertArray(reader);
+ localCertificates = readCertArray(reader);
} else {
cipherSuite = null;
peerCertificates = null;
@@ -419,7 +426,7 @@
}
public void writeTo(DiskLruCache.Editor editor) throws IOException {
- OutputStream out = editor.newOutputStream(0);
+ OutputStream out = editor.newOutputStream(ENTRY_METADATA);
Writer writer = new BufferedWriter(new OutputStreamWriter(out, Charsets.UTF_8));
writer.write(uri + '\n');
@@ -450,17 +457,8 @@
return uri.startsWith("https://");
}
- private int readInt(InputStream in) throws IOException {
- String intString = Streams.readAsciiLine(in);
- try {
- return Integer.parseInt(intString);
- } catch (NumberFormatException e) {
- throw new IOException("expected an int but was \"" + intString + "\"");
- }
- }
-
- private Certificate[] readCertArray(InputStream in) throws IOException {
- int length = readInt(in);
+ private Certificate[] readCertArray(StrictLineReader reader) throws IOException {
+ int length = reader.readInt();
if (length == -1) {
return null;
}
@@ -468,7 +466,7 @@
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate[] result = new Certificate[length];
for (int i = 0; i < result.length; i++) {
- String line = Streams.readAsciiLine(in);
+ String line = reader.readLine();
byte[] bytes = Base64.decode(line.getBytes(Charsets.US_ASCII));
result[i] = certificateFactory.generateCertificate(
new ByteArrayInputStream(bytes));
diff --git a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
index a59df55..260a9ad 100644
--- a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
+++ b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
@@ -36,6 +36,7 @@
import java.util.List;
import java.util.Map;
import libcore.io.Base64;
+import libcore.io.IoUtils;
/**
* This implementation uses HttpEngine to send requests and receive responses.
@@ -87,6 +88,14 @@
@Override public final void disconnect() {
// Calling disconnect() before a connection exists should have no effect.
if (httpEngine != null) {
+ // We close the response body here instead of in
+ // HttpEngine.release because that is called when input
+ // has been completely read from the underlying socket.
+ // However the response body can be a GZIPInputStream that
+ // still has unread data.
+ if (httpEngine.hasResponse()) {
+ IoUtils.closeQuietly(httpEngine.getResponseBody());
+ }
httpEngine.release(false);
}
}
diff --git a/luni/src/main/java/libcore/util/BasicLruCache.java b/luni/src/main/java/libcore/util/BasicLruCache.java
index 13ab3db..75e4a75 100644
--- a/luni/src/main/java/libcore/util/BasicLruCache.java
+++ b/luni/src/main/java/libcore/util/BasicLruCache.java
@@ -43,7 +43,7 @@
*/
public synchronized final V get(K key) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
V result = map.get(key);
@@ -68,8 +68,10 @@
* no longer cached, it has not been passed to {@link #entryEvicted}.
*/
public synchronized final V put(K key, V value) {
- if (key == null || value == null) {
- throw new NullPointerException();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (value == null) {
+ throw new NullPointerException("value == null");
}
V previous = map.put(key, value);
diff --git a/luni/src/main/java/libcore/util/Objects.java b/luni/src/main/java/libcore/util/Objects.java
index 7817316..573b973 100644
--- a/luni/src/main/java/libcore/util/Objects.java
+++ b/luni/src/main/java/libcore/util/Objects.java
@@ -16,6 +16,10 @@
package libcore.util;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
public final class Objects {
private Objects() {}
@@ -29,4 +33,64 @@
public static int hashCode(Object o) {
return (o == null) ? 0 : o.hashCode();
}
+
+ /**
+ * Returns a string reporting the value of each declared field, via reflection.
+ * Static and transient fields are automatically skipped. Produces output like
+ * "SimpleClassName[integer=1234,string="hello",character='c',intArray=[1,2,3]]".
+ */
+ public static String toString(Object o) {
+ Class<?> c = o.getClass();
+ StringBuilder sb = new StringBuilder();
+ sb.append(c.getSimpleName()).append('[');
+ int i = 0;
+ for (Field f : c.getDeclaredFields()) {
+ if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) {
+ continue;
+ }
+ f.setAccessible(true);
+ try {
+ Object value = f.get(o);
+
+ if (i++ > 0) {
+ sb.append(',');
+ }
+
+ sb.append(f.getName());
+ sb.append('=');
+
+ if (value.getClass().isArray()) {
+ if (value.getClass() == boolean[].class) {
+ sb.append(Arrays.toString((boolean[]) value));
+ } else if (value.getClass() == byte[].class) {
+ sb.append(Arrays.toString((byte[]) value));
+ } else if (value.getClass() == char[].class) {
+ sb.append(Arrays.toString((char[]) value));
+ } else if (value.getClass() == double[].class) {
+ sb.append(Arrays.toString((double[]) value));
+ } else if (value.getClass() == float[].class) {
+ sb.append(Arrays.toString((float[]) value));
+ } else if (value.getClass() == int[].class) {
+ sb.append(Arrays.toString((int[]) value));
+ } else if (value.getClass() == long[].class) {
+ sb.append(Arrays.toString((long[]) value));
+ } else if (value.getClass() == short[].class) {
+ sb.append(Arrays.toString((short[]) value));
+ } else {
+ sb.append(Arrays.toString((Object[]) value));
+ }
+ } else if (value.getClass() == Character.class) {
+ sb.append('\'').append(value).append('\'');
+ } else if (value.getClass() == String.class) {
+ sb.append('"').append(value).append('"');
+ } else {
+ sb.append(value);
+ }
+ } catch (IllegalAccessException unexpected) {
+ throw new AssertionError(unexpected);
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
}
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 5a8caf2..ac48b23 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -51,29 +51,46 @@
private final byte[] mTypes;
private final byte[] mIsDsts;
private final boolean mUseDst;
+ private final int mDstSavings; // Implements TimeZone.getDSTSavings.
- ZoneInfo(String name, int[] transitions, byte[] type, int[] gmtOffsets, byte[] isDsts) {
+ ZoneInfo(String name, int[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts) {
mTransitions = transitions;
- mTypes = type;
+ mTypes = types;
mIsDsts = isDsts;
setID(name);
- // Use the latest non-daylight offset (if any) as the raw offset.
- int lastStd;
- for (lastStd = mTransitions.length - 1; lastStd >= 0; lastStd--) {
- if (mIsDsts[mTypes[lastStd] & 0xff] == 0) {
- break;
+ // Find the latest daylight and standard offsets (if any).
+ int lastStd = 0;
+ boolean haveStd = false;
+ int lastDst = 0;
+ boolean haveDst = false;
+ for (int i = mTransitions.length - 1; (!haveStd || !haveDst) && i >= 0; --i) {
+ int type = mTypes[i] & 0xff;
+ if (!haveStd && mIsDsts[type] == 0) {
+ haveStd = true;
+ lastStd = i;
+ }
+ if (!haveDst && mIsDsts[type] != 0) {
+ haveDst = true;
+ lastDst = i;
}
}
- if (lastStd < 0) {
- lastStd = 0;
- }
+
+ // Use the latest non-daylight offset (if any) as the raw offset.
if (lastStd >= mTypes.length) {
mRawOffset = gmtOffsets[0];
} else {
mRawOffset = gmtOffsets[mTypes[lastStd] & 0xff];
}
+ // Use the latest transition's pair of offsets to compute the DST savings.
+ // This isn't generally useful, but it's exposed by TimeZone.getDSTSavings.
+ if (lastDst >= mTypes.length) {
+ mDstSavings = 0;
+ } else {
+ mDstSavings = Math.abs(gmtOffsets[mTypes[lastStd] & 0xff] - gmtOffsets[mTypes[lastDst] & 0xff]) * 1000;
+ }
+
// Cache the oldest known raw offset, in case we're asked about times that predate our
// transition data.
int firstStd = -1;
@@ -111,6 +128,7 @@
}
mUseDst = usesDst;
+ // tzdata uses seconds, but Java uses milliseconds.
mRawOffset *= 1000;
mEarliestRawOffset = earliestRawOffset * 1000;
}
@@ -185,6 +203,10 @@
mRawOffset = off;
}
+ @Override public int getDSTSavings() {
+ return mUseDst ? mDstSavings: 0;
+ }
+
@Override public boolean useDaylightTime() {
return mUseDst;
}
@@ -235,7 +257,7 @@
StringBuilder sb = new StringBuilder();
// First the basics...
sb.append(getClass().getName() + "[" + getID() + ",mRawOffset=" + mRawOffset +
- ",mUseDst=" + mUseDst + "]");
+ ",mUseDst=" + mUseDst + ",mDstSavings=" + mDstSavings + "]");
// ...followed by a zdump(1)-like description of all our transition data.
sb.append("\n");
Formatter f = new Formatter(sb);
diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java
index 69b6784..cc7cc4f 100644
--- a/luni/src/main/java/libcore/util/ZoneInfoDB.java
+++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java
@@ -180,7 +180,7 @@
}
}
- private static TimeZone makeTimeZone(String id) throws IOException {
+ public static TimeZone makeTimeZone(String id) throws IOException {
// Work out where in the big data file this time zone is.
int index = Arrays.binarySearch(ids, id);
if (index < 0) {
@@ -259,17 +259,6 @@
}
}
- public static TimeZone getTimeZone(String id) {
- if (id == null) {
- return null;
- }
- try {
- return makeTimeZone(id);
- } catch (IOException ignored) {
- return null;
- }
- }
-
public static String getVersion() {
return VERSION;
}
diff --git a/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java b/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java
index 270f63b..151b403 100644
--- a/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java
+++ b/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java
@@ -115,8 +115,10 @@
@Override
public int engineUpdate(ByteBuffer input, ByteBuffer output)
throws ShortBufferException {
- if (input == null || output == null) {
- throw new NullPointerException();
+ if (input == null) {
+ throw new NullPointerException("input == null");
+ } else if (output == null) {
+ throw new NullPointerException("output == null");
}
int result = input.limit() - input.position();
try {
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java b/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java
deleted file mode 100644
index 35e6c62..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.harmony.luni.util;
-
-import java.util.AbstractCollection;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.ConcurrentModificationException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-/**
- *
- * Reductive hash with two keys
- *
- */
-public class TwoKeyHashMap<E, K, V> extends AbstractMap<String, V> {
-
- static final float DEFAULT_LOAD_FACTOR = 0.75f;
- static final int DEFAULT_INITIAL_SIZE = 16;
-
- private Set<Map.Entry<String, V>> entrySet;
- private Collection<V> values;
- private int size;
- private int arrSize;
- private int modCount;
-
- private Entry<E, K, V>[] arr;
-
- private float loadFactor;
- int threshold = 0;
-
- /**
- * Constructs an empty HashMap
- */
- public TwoKeyHashMap() {
- this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR);
- }
-
- /**
- * Constructs an empty HashMap
- *
- * @param initialCapacity
- */
- public TwoKeyHashMap(int initialCapacity) {
- this(initialCapacity, DEFAULT_LOAD_FACTOR);
- }
-
- /**
- * Constructs an empty HashMap
- *
- * @param initialCapacity
- * @param initialLoadFactor
- */
- @SuppressWarnings("unchecked")
- public TwoKeyHashMap(int initialCapacity, float initialLoadFactor) {
- if (initialCapacity < 0) {
- throw new IllegalArgumentException("initialCapacity should be >= 0");
- }
- if (initialLoadFactor <= 0) {
- throw new IllegalArgumentException(
- "initialLoadFactor should be > 0");
- }
- loadFactor = initialLoadFactor;
- if (initialCapacity == Integer.MAX_VALUE) {
- initialCapacity--;
- }
- arrSize = initialCapacity > 0 ? initialCapacity : 1;
- threshold = (int) (arrSize * loadFactor);
- arr = new Entry[arrSize + 1];
- }
-
- /**
- * Returns a collection view of the values
- */
- public Collection<V> values() {
- if (values == null) {
- values = new ValuesCollectionImpl();
- }
- return values;
- }
-
- /**
- * Returns a collection view of the mappings
- */
- public Set<Map.Entry<String, V>> entrySet() {
- if (entrySet == null) {
- entrySet = new EntrySetImpl();
- }
- return entrySet;
- }
-
- /**
- * Clears the map
- */
- public void clear() {
- modCount++;
- size = 0;
- Arrays.fill(arr, 0, arr.length, null);
- }
-
- /**
- * Removes the mapping for the keys
- *
- * @param key1
- * @param key2
- * @return
- */
- public V remove(Object key1, Object key2) {
- Entry<E, K, V> e = removeEntry(key1, key2);
- return (e != null) ? e.value : null;
- }
-
- /**
- * Associates the specified value with the specified keys in this map
- *
- * @param key1
- * @param key2
- * @param value
- * @return
- */
- public V put(E key1, K key2, V value) {
- if (key1 == null && key2 == null) {
- int index = arrSize;
- if (arr[index] == null) {
- arr[index] = createEntry(0, null, null, value, null);
- size++;
- modCount++;
- return null;
- } else {
- V oldValue = arr[index].value;
- arr[index].value = value;
- return oldValue;
- }
- }
-
- int hash = key1.hashCode() + key2.hashCode();
- int index = (hash & 0x7fffffff) % arrSize;
- Entry<E, K, V> e = arr[index];
-
- while (e != null) {
- if (hash == e.hash && key1.equals(e.getKey1())
- && key2.equals(e.getKey2())) {
- V oldValue = e.value;
- e.value = value;
- return oldValue;
- }
- e = e.next;
- }
-
- arr[index] = createEntry(hash, key1, key2, value, arr[index]);
- size++;
- modCount++;
-
- if (size > threshold) {
- rehash();
- }
- return null;
- }
-
- /**
- * Rehash the map
- *
- */
- @SuppressWarnings("unchecked")
- void rehash() {
- int newArrSize = (arrSize + 1) * 2 + 1;
- if (newArrSize < 0) {
- newArrSize = Integer.MAX_VALUE - 1;
- }
- Entry<E, K, V>[] newArr = new Entry[newArrSize + 1];
-
- for (int i = 0; i < arr.length - 1; i++) {
- Entry<E, K, V> entry = arr[i];
- while (entry != null) {
- Entry<E, K, V> next = entry.next;
-
- int newIndex = (entry.hash & 0x7fffffff) % newArrSize;
- entry.next = newArr[newIndex];
- newArr[newIndex] = entry;
-
- entry = next;
- }
- }
- newArr[newArrSize] = arr[arrSize]; // move null entry
- arrSize = newArrSize;
-
- // The maximum array size is reached, increased loadFactor
- // will keep array from further growing
- if (arrSize == Integer.MAX_VALUE) {
- loadFactor *= 10;
- }
- threshold = (int) (arrSize * loadFactor);
- arr = newArr;
- }
-
- /**
- * Returns true if this map contains a mapping for {@code key1} and {@code key2}.
- */
- public boolean containsKey(Object key1, Object key2) {
- return findEntry(key1, key2) != null;
- }
-
- /**
- * Return the value by keys
- *
- * @param key1
- * @param key2
- * @return
- */
- public V get(Object key1, Object key2) {
- Entry<E, K, V> e = findEntry(key1, key2);
- if (e != null) {
- return e.value;
- }
- return null;
- }
-
- /**
- * Returns true if this map contains no key-value mappings
- */
- public boolean isEmpty() {
- return size == 0;
- }
-
- /**
- * Returns the number of mappings
- */
- public int size() {
- return size;
- }
-
- /**
- * Creates new entry
- *
- * @param hashCode
- * @param key1
- * @param key2
- * @param value
- * @param next
- * @return
- */
- Entry<E, K, V> createEntry(int hashCode, E key1, K key2, V value,
- Entry<E, K, V> next) {
- return new Entry<E, K, V>(hashCode, key1, key2, value, next);
- }
-
- /**
- * Creates entries iterator
- *
- * @return
- */
- Iterator<Map.Entry<String, V>> createEntrySetIterator() {
- return new EntryIteratorImpl();
- }
-
- /**
- * Creates values iterator
- *
- * @return
- */
- Iterator<V> createValueCollectionIterator() {
- return new ValueIteratorImpl();
- }
-
- /**
- * Entry implementation for the TwoKeyHashMap class
- *
- */
- public static class Entry<E, K, V> implements Map.Entry<String, V> {
- int hash;
- E key1;
- K key2;
- V value;
- Entry<E, K, V> next;
-
- public Entry(int hash, E key1, K key2, V value, Entry<E, K, V> next) {
- this.hash = hash;
- this.key1 = key1;
- this.key2 = key2;
- this.value = value;
- this.next = next;
- }
-
- public String getKey() {
- return key1.toString() + key2.toString();
- }
-
- public E getKey1() {
- return key1;
- }
-
- public K getKey2() {
- return key2;
- }
-
- public V getValue() {
- return value;
- }
-
- public V setValue(V value) {
- V oldValue = this.value;
- this.value = value;
- return oldValue;
- }
-
- public boolean equals(Object obj) {
- if (!(obj instanceof Entry)) {
- return false;
- }
-
- Entry<?, ?, ?> e = (Entry<?, ?, ?>) obj;
- Object getKey1 = e.getKey1();
- Object getKey2 = e.getKey2();
- Object getValue = e.getValue();
- if ((key1 == null && getKey1 != null)
- || (key2 == null && getKey2 != null)
- || (value == null && getValue != null)
- || !key1.equals(e.getKey1()) || !key2.equals(e.getKey2())
- || !value.equals(getValue)) {
- return false;
- }
- return true;
- }
-
- public int hashCode() {
- int hash1 = (key1 == null ? 0 : key1.hashCode());
- int hash2 = (key2 == null ? 0 : key2.hashCode());
- return (hash1 + hash2) ^ (value == null ? 0 : value.hashCode());
- }
-
- }
-
- class EntrySetImpl extends AbstractSet<Map.Entry<String, V>> {
- public int size() {
- return size;
- }
-
- public void clear() {
- TwoKeyHashMap.this.clear();
- }
-
- public boolean isEmpty() {
- return size == 0;
- }
-
- public boolean contains(Object obj) {
- if (!(obj instanceof Entry)) {
- return false;
- }
-
- Entry<?, ?, ?> entry = (Entry<?, ?, ?>) obj;
- Entry<E, K, V> entry2 = findEntry(entry.getKey1(), entry.getKey2());
- if (entry2 == null) {
- return false;
- }
- Object value = entry.getValue();
- Object value2 = entry2.getValue();
- return value == null ? value2 == null : value.equals(value2);
- }
-
- public boolean remove(Object obj) {
- if (!(obj instanceof Entry)) {
- return false;
- }
- return removeEntry(((Entry) obj).getKey1(), ((Entry) obj).getKey2()) != null;
- }
-
- public Iterator<Map.Entry<String, V>> iterator() {
- return createEntrySetIterator();
- }
- }
-
- // Iterates Entries inside the Map
- class EntryIteratorImpl implements Iterator<Map.Entry<String, V>> {
- private int startModCount;
- private boolean found;
- private int curr = -1;
- private int returned_index = -1;
- private Entry<E, K, V> curr_entry;
- private Entry<E, K, V> returned_entry;
-
- EntryIteratorImpl() {
- startModCount = modCount;
- }
-
- public boolean hasNext() {
- if (found) {
- return true;
- }
- if (curr_entry != null) {
- curr_entry = curr_entry.next;
- }
- if (curr_entry == null) {
- for (curr++; curr < arr.length && arr[curr] == null; curr++) {
- }
-
- if (curr < arr.length) {
- curr_entry = arr[curr];
- }
- }
- return found = (curr_entry != null);
- }
-
- public Map.Entry<String, V> next() {
- if (modCount != startModCount) {
- throw new ConcurrentModificationException();
- }
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
-
- found = false;
- returned_index = curr;
- returned_entry = curr_entry;
- return (Map.Entry<String, V>) curr_entry;
- }
-
- public void remove() {
- if (returned_index == -1) {
- throw new IllegalStateException();
- }
-
- if (modCount != startModCount) {
- throw new ConcurrentModificationException();
- }
-
- Entry<E, K, V> p = null;
- Entry<E, K, V> e = arr[returned_index];
- while (e != returned_entry) {
- p = e;
- e = e.next;
- }
- if (p != null) {
- p.next = returned_entry.next;
- } else {
- arr[returned_index] = returned_entry.next;
- }
- size--;
- modCount++;
- startModCount++;
- returned_index = -1;
- }
- }
-
- private final Entry<E, K, V> findEntry(Object key1, Object key2) {
- if (key1 == null && key2 == null) {
- return arr[arrSize];
- }
-
- int hash = key1.hashCode() + key2.hashCode();
- int index = (hash & 0x7fffffff) % arrSize;
- Entry<E, K, V> e = arr[index];
-
- while (e != null) {
- if (hash == e.hash && key1.equals(e.getKey1())
- && key2.equals(e.getKey2())) {
- return e;
- }
- e = e.next;
- }
- return null;
- }
-
- // Removes entry
- private final Entry<E, K, V> removeEntry(Object key1, Object key2) {
- if (key1 == null && key2 == null) {
- int index = arrSize;
- if (arr[index] != null) {
- Entry<E, K, V> ret = arr[index];
- arr[index] = null;
- size--;
- modCount++;
- return ret;
- }
- return null;
- }
-
- int hash = key1.hashCode() + key2.hashCode();
- int index = (hash & 0x7fffffff) % arrSize;
-
- Entry<E, K, V> e = arr[index];
- Entry<E, K, V> prev = e;
- while (e != null) {
- if (hash == e.hash && key1.equals(e.getKey1())
- && key2.equals(e.getKey2())) {
- if (prev == e) {
- arr[index] = e.next;
- } else {
- prev.next = e.next;
- }
- size--;
- modCount++;
- return e;
- }
-
- prev = e;
- e = e.next;
- }
- return null;
- }
-
- /**
- * An instance is returned by the values() call.
- */
- class ValuesCollectionImpl extends AbstractCollection<V> {
- public int size() {
- return size;
- }
-
- public void clear() {
- TwoKeyHashMap.this.clear();
- }
-
- public boolean isEmpty() {
- return size == 0;
- }
-
- public Iterator<V> iterator() {
- return createValueCollectionIterator();
- }
-
- public boolean contains(Object obj) {
- return containsValue(obj);
- }
- }
-
- class ValueIteratorImpl implements Iterator<V> {
- private EntryIteratorImpl itr;
-
- ValueIteratorImpl() {
- this.itr = new EntryIteratorImpl();
- }
-
- public V next() {
- return itr.next().getValue();
- }
-
- public void remove() {
- itr.remove();
- }
-
- public boolean hasNext() {
- return itr.hasNext();
- }
- }
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/SystemScope.java b/luni/src/main/java/org/apache/harmony/security/SystemScope.java
index 89cf56b..bf4f849 100644
--- a/luni/src/main/java/org/apache/harmony/security/SystemScope.java
+++ b/luni/src/main/java/org/apache/harmony/security/SystemScope.java
@@ -79,7 +79,7 @@
*/
public synchronized Identity getIdentity(String name) {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
return (Identity) names.get(name);
}
diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
index 8a67ac2..f1dd43c 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
@@ -93,15 +93,15 @@
/** used to test for cache hit */
private final String algorithm;
/** used to test for cache validity */
- private final int refreshNumber;
+ private final int cacheVersion;
/** cached result */
private final Provider.Service service;
private ServiceCacheEntry(String algorithm,
- int refreshNumber,
+ int cacheVersion,
Provider.Service service) {
this.algorithm = algorithm;
- this.refreshNumber = refreshNumber;
+ this.cacheVersion = cacheVersion;
this.service = service;
}
}
@@ -134,12 +134,12 @@
if (algorithm == null) {
throw new NoSuchAlgorithmException("Null algorithm name");
}
- Services.refresh();
+ int newCacheVersion = Services.getCacheVersion();
Provider.Service service;
ServiceCacheEntry cacheEntry = this.serviceCache;
if (cacheEntry != null
&& cacheEntry.algorithm.equalsIgnoreCase(algorithm)
- && Services.refreshNumber == cacheEntry.refreshNumber) {
+ && newCacheVersion == cacheEntry.cacheVersion) {
service = cacheEntry.service;
} else {
if (Services.isEmpty()) {
@@ -150,7 +150,7 @@
if (service == null) {
throw notFound(serviceName, algorithm);
}
- this.serviceCache = new ServiceCacheEntry(algorithm, Services.refreshNumber, service);
+ this.serviceCache = new ServiceCacheEntry(algorithm, newCacheVersion, service);
}
return new SpiAndProvider(service.newInstance(param), service.getProvider());
}
diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
index d97e0f4..4fe0d44 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
@@ -15,11 +15,6 @@
* limitations under the License.
*/
-/**
-* @author Boris V. Kuznetsov
-* @version $Revision$
-*/
-
package org.apache.harmony.security.fortress;
import java.security.Provider;
@@ -34,87 +29,83 @@
/**
* This class contains information about all registered providers and preferred
* implementations for all "serviceName.algName".
- *
*/
-
public class Services {
- // The HashMap that contains information about preferred implementations for
- // all serviceName.algName in the registered providers.
- // Set the initial size to 600 so we don't grow to 1024 by default because
- // initialization adds a few entries more than the growth threshold.
+ /**
+ * The HashMap that contains information about preferred implementations for
+ * all serviceName.algName in the registered providers.
+ * Set the initial size to 600 so we don't grow to 1024 by default because
+ * initialization adds a few entries more than the growth threshold.
+ */
private static final Map<String, Provider.Service> services
= new HashMap<String, Provider.Service>(600);
- // Save default SecureRandom service as well.
- // Avoids similar provider/services iteration in SecureRandom constructor
- private static Provider.Service secureRandom;
-
- // Need refresh flag
- private static boolean needRefresh; // = false;
/**
- * Refresh number
+ * Save default SecureRandom service as well.
+ * Avoids similar provider/services iteration in SecureRandom constructor.
*/
- static int refreshNumber = 1;
+ private static Provider.Service cachedSecureRandomService;
- // Registered providers
+ /**
+ * Need refresh flag.
+ */
+ private static boolean needRefresh;
+
+ /**
+ * The cacheVersion is changed on every update of service
+ * information. It is used by external callers to validate their
+ * own caches of Service information.
+ */
+ private static int cacheVersion = 1;
+
+ /**
+ * Registered providers.
+ */
private static final List<Provider> providers = new ArrayList<Provider>(20);
- // Hash for quick provider access by name
+ /**
+ * Hash for quick provider access by name.
+ */
private static final Map<String, Provider> providersNames = new HashMap<String, Provider>(20);
static {
- loadProviders();
- }
-
- // Load statically registered providers and init Services Info
- private static void loadProviders() {
String providerClassName = null;
int i = 1;
ClassLoader cl = ClassLoader.getSystemClassLoader();
- Provider p;
- while ((providerClassName = Security.getProperty("security.provider."
- + i++)) != null) {
+ while ((providerClassName = Security.getProperty("security.provider." + i++)) != null) {
try {
- p = (Provider) Class
- .forName(providerClassName.trim(), true, cl)
- .newInstance();
+ Class providerClass = Class.forName(providerClassName.trim(), true, cl);
+ Provider p = (Provider) providerClass.newInstance();
providers.add(p);
providersNames.put(p.getName(), p);
initServiceInfo(p);
- } catch (ClassNotFoundException e) { // ignore Exceptions
- } catch (IllegalAccessException e) {
- } catch (InstantiationException e) {
+ } catch (ClassNotFoundException ignored) {
+ } catch (IllegalAccessException ignored) {
+ } catch (InstantiationException ignored) {
}
}
Engine.door.renumProviders();
}
/**
- * Returns registered providers
- *
- * @return
+ * Returns a copy of the registered providers as an array.
*/
- public static Provider[] getProviders() {
+ public static synchronized Provider[] getProviders() {
return providers.toArray(new Provider[providers.size()]);
}
/**
- * Returns registered providers as List
- *
- * @return
+ * Returns a copy of the registered providers as a list.
*/
- public static List<Provider> getProvidersList() {
+ public static synchronized List<Provider> getProvidersList() {
return new ArrayList<Provider>(providers);
}
/**
- * Returns the provider with the specified name
- *
- * @param name
- * @return
+ * Returns the provider with the specified name.
*/
- public static Provider getProvider(String name) {
+ public static synchronized Provider getProvider(String name) {
if (name == null) {
return null;
}
@@ -122,13 +113,9 @@
}
/**
- * Inserts a provider at a specified position
- *
- * @param provider
- * @param position
- * @return
+ * Inserts a provider at a specified 1-based position.
*/
- public static int insertProviderAt(Provider provider, int position) {
+ public static synchronized int insertProviderAt(Provider provider, int position) {
int size = providers.size();
if ((position < 1) || (position > size)) {
position = size + 1;
@@ -140,98 +127,91 @@
}
/**
- * Removes the provider
- *
- * @param providerNumber
+ * Removes the provider at the specified 1-based position.
*/
- public static void removeProvider(int providerNumber) {
+ public static synchronized void removeProvider(int providerNumber) {
Provider p = providers.remove(providerNumber - 1);
providersNames.remove(p.getName());
setNeedRefresh();
}
/**
- *
* Adds information about provider services into HashMap.
- *
- * @param p
*/
- public static void initServiceInfo(Provider p) {
- for (Provider.Service serv : p.getServices()) {
- String type = serv.getType();
- if (secureRandom == null && type.equals("SecureRandom")) {
- secureRandom = serv;
+ public static synchronized void initServiceInfo(Provider p) {
+ for (Provider.Service service : p.getServices()) {
+ String type = service.getType();
+ if (cachedSecureRandomService == null && type.equals("SecureRandom")) {
+ cachedSecureRandomService = service;
}
- String key = type + "." + serv.getAlgorithm().toUpperCase(Locale.US);
+ String key = type + "." + service.getAlgorithm().toUpperCase(Locale.US);
if (!services.containsKey(key)) {
- services.put(key, serv);
+ services.put(key, service);
}
- for (String alias : Engine.door.getAliases(serv)) {
+ for (String alias : Engine.door.getAliases(service)) {
key = type + "." + alias.toUpperCase(Locale.US);
if (!services.containsKey(key)) {
- services.put(key, serv);
+ services.put(key, service);
}
}
}
}
/**
- *
- * Updates services hashtable for all registered providers
- *
+ * Returns true if services contain any provider information.
*/
- public static void updateServiceInfo() {
- services.clear();
- secureRandom = null;
- for (Provider p : providers) {
- initServiceInfo(p);
- }
- needRefresh = false;
- }
-
- /**
- * Returns true if services contain any provider information
- * @return
- */
- public static boolean isEmpty() {
+ public static synchronized boolean isEmpty() {
return services.isEmpty();
}
/**
- * Returns service description.
- * Call refresh() before.
+ * Looks up the requested service by type and algorithm. The
+ * service key should be provided in the same format used when
+ * registering a service with a provider, for example,
+ * "KeyFactory.RSA".
*
- * @param key in the format TYPE.ALGORITHM
- * @return
+ * Callers can cache the returned service information but such
+ * caches should be validated against the result of
+ * Service.getCacheVersion() before use.
*/
- public static Provider.Service getService(String key) {
+ public static synchronized Provider.Service getService(String key) {
return services.get(key);
}
/**
* Returns the default SecureRandom service description.
- * Call refresh() before.
*/
- public static Provider.Service getSecureRandomService() {
- return secureRandom;
+ public static synchronized Provider.Service getSecureRandomService() {
+ getCacheVersion(); // used for side effect of updating cache if needed
+ return cachedSecureRandomService;
}
/**
- * Set flag needRefresh
- *
+ * In addition to being used here when the list of providers
+ * changes, this method is also used by the Provider
+ * implementation to indicate that a provides list of services has
+ * changed.
*/
- public static void setNeedRefresh() {
+ public static synchronized void setNeedRefresh() {
needRefresh = true;
}
/**
- * Refresh services info
- *
+ * Returns the current cache version. This has the possible side
+ * effect of updating the cache if needed.
*/
- public static void refresh() {
+ public static synchronized int getCacheVersion() {
if (needRefresh) {
- refreshNumber++;
- updateServiceInfo();
+ cacheVersion++;
+ synchronized (services) {
+ services.clear();
+ }
+ cachedSecureRandomService = null;
+ for (Provider p : providers) {
+ initServiceInfo(p);
+ }
+ needRefresh = false;
}
+ return cacheVersion;
}
}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java
index 134d8f9..68ec38a 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java
@@ -237,7 +237,7 @@
*/
public X509CRLEntry getRevokedCertificate(X509Certificate certificate) {
if (certificate == null) {
- throw new NullPointerException();
+ throw new NullPointerException("certificate == null");
}
if (!entriesRetrieved) {
retrieveEntries();
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java
index d2a9c6d..2958e00 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java
@@ -57,7 +57,7 @@
protected Object engineGetParameter(String param)
throws InvalidParameterException {
if (param == null) {
- throw new NullPointerException();
+ throw new NullPointerException("param == null");
}
return null;
}
diff --git a/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java b/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java
index be43ba7..4985aff 100644
--- a/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java
+++ b/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java
@@ -70,6 +70,34 @@
return aki;
}
+ /**
+ * The key identifier for the authority.
+ *
+ * @return key identifier or {@code null}
+ */
+ public byte[] getKeyIdentifier() {
+ return keyIdentifier;
+ }
+
+ /**
+ * The GeneralNames for this authority key identifier.
+ *
+ * @return names for the authority certificate issuer or {@code null}
+ */
+ public GeneralNames getAuthorityCertIssuer() {
+ return authorityCertIssuer;
+ }
+
+ /**
+ * The serial number of the certificate identified by this authority key
+ * identifier.
+ *
+ * @return authority's certificate serial number or {@code null}
+ */
+ public BigInteger getAuthorityCertSerialNumber() {
+ return authorityCertSerialNumber;
+ }
+
@Override public byte[] getEncoded() {
if (encoding == null) {
encoding = ASN1.encode(this);
@@ -110,10 +138,10 @@
@Override protected Object getDecodedObject(BerInputStream in) throws IOException {
Object[] values = (Object[]) in.content;
- byte[] enc = (byte[]) values[2];
+ byte[] bytes = (byte[]) values[2];
BigInteger authorityCertSerialNumber = null;
- if (enc != null) {
- authorityCertSerialNumber = new BigInteger(enc);
+ if (bytes != null) {
+ authorityCertSerialNumber = new BigInteger(bytes);
}
return new AuthorityKeyIdentifier((byte[]) values[0],
diff --git a/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java b/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java
index 7415002..1db9598 100644
--- a/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java
+++ b/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java
@@ -58,6 +58,13 @@
return res;
}
+ /**
+ * The key identifier for this subject.
+ */
+ public byte[] getKeyIdentifier() {
+ return keyIdentifier;
+ }
+
@Override public byte[] getEncoded() {
if (encoding == null) {
encoding = ASN1OctetString.getInstance().encode(keyIdentifier);
diff --git a/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java b/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java
index b92fc50..5ec632c 100644
--- a/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java
+++ b/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java
@@ -76,10 +76,10 @@
public int getIndex(String uri, String localName) {
if (uri == null) {
- throw new NullPointerException("uri");
+ throw new NullPointerException("uri == null");
}
if (localName == null) {
- throw new NullPointerException("local name");
+ throw new NullPointerException("localName == null");
}
int pointer = getPointer();
if (pointer == 0) {
@@ -90,7 +90,7 @@
public int getIndex(String qName) {
if (qName == null) {
- throw new NullPointerException("uri");
+ throw new NullPointerException("qName == null");
}
int pointer = getPointer();
if (pointer == 0) {
@@ -101,10 +101,10 @@
public String getType(String uri, String localName) {
if (uri == null) {
- throw new NullPointerException("uri");
+ throw new NullPointerException("uri == null");
}
if (localName == null) {
- throw new NullPointerException("local name");
+ throw new NullPointerException("localName == null");
}
return getIndex(uri, localName) == -1 ? null : CDATA;
}
@@ -115,10 +115,10 @@
public String getValue(String uri, String localName) {
if (uri == null) {
- throw new NullPointerException("uri");
+ throw new NullPointerException("uri == null");
}
if (localName == null) {
- throw new NullPointerException("local name");
+ throw new NullPointerException("localName == null");
}
int pointer = getPointer();
if (pointer == 0) {
@@ -129,7 +129,7 @@
public String getValue(String qName) {
if (qName == null) {
- throw new NullPointerException("qName");
+ throw new NullPointerException("qName == null");
}
int pointer = getPointer();
if (pointer == 0) {
diff --git a/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java b/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
index 1f1293b..af4002f 100644
--- a/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
@@ -697,7 +697,7 @@
public final Object setUserData(String key, Object data, UserDataHandler handler) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
Map<String, UserData> map = document.getUserDataMap(this);
UserData previous = data == null
@@ -708,7 +708,7 @@
public final Object getUserData(String key) {
if (key == null) {
- throw new NullPointerException();
+ throw new NullPointerException("key == null");
}
Map<String, UserData> map = document.getUserDataMapForRead(this);
UserData userData = map.get(key);
diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java
index 8efaa30..debbb20 100644
--- a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java
@@ -42,7 +42,7 @@
@Override
public boolean getFeature(String name) throws ParserConfigurationException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (NAMESPACES.equals(name)) {
@@ -90,7 +90,7 @@
public void setFeature(String name, boolean value)
throws ParserConfigurationException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (NAMESPACES.equals(name)) {
diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java
index 08657b9..e2e3778 100644
--- a/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java
@@ -41,7 +41,7 @@
@Override
public boolean getFeature(String name) throws SAXNotRecognizedException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (!name.startsWith("http://xml.org/sax/features/")) {
@@ -86,7 +86,7 @@
@Override
public void setFeature(String name, boolean value) throws SAXNotRecognizedException {
if (name == null) {
- throw new NullPointerException();
+ throw new NullPointerException("name == null");
}
if (!name.startsWith("http://xml.org/sax/features/")) {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
index 0af1944..732457b 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
@@ -18,14 +18,12 @@
package org.apache.harmony.xnet.provider.jsse;
import java.io.IOException;
-import java.security.AccessController;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
-import java.security.PrivilegedExceptionAction;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -512,7 +510,7 @@
// send certificate verify for all certificates except those containing
// fixed DH parameters
- if (clientCert != null && !clientKeyExchange.isEmpty()) {
+ if (clientCert != null && clientCert.certs.length > 0 && !clientKeyExchange.isEmpty()) {
// Certificate verify
String authType = clientKey.getAlgorithm();
DigitalSignature ds = new DigitalSignature(authType);
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java
index 6619d1d..0f1fe24 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java
@@ -121,7 +121,7 @@
*/
private static String fileName(String host, int port) {
if (host == null) {
- throw new NullPointerException("host");
+ throw new NullPointerException("host == null");
}
return host + "." + port;
}
@@ -182,7 +182,7 @@
byte[] sessionData) {
String host = session.getPeerHost();
if (sessionData == null) {
- throw new NullPointerException("sessionData");
+ throw new NullPointerException("sessionData == null");
}
String name = fileName(host, session.getPeerPort());
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
index a2688e2..2fbbef7 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
@@ -19,8 +19,6 @@
import java.io.PrintStream;
import java.util.Locale;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import libcore.util.EmptyArray;
/**
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index 8f071f0..84f33f2 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -22,6 +22,7 @@
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
@@ -31,6 +32,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import javax.crypto.BadPaddingException;
import javax.net.ssl.SSLException;
import javax.security.auth.x500.X500Principal;
import libcore.io.Memory;
@@ -52,6 +54,8 @@
public static native int ENGINE_by_id(String id);
+ public static native int ENGINE_add(int e);
+
public static native int ENGINE_init(int e);
public static native int ENGINE_finish(int e);
@@ -84,6 +88,20 @@
public static native int RSA_generate_key_ex(int modulusBits, byte[] publicExponent);
+ public static native int RSA_size(int pkey);
+
+ public static native int RSA_private_encrypt(int flen, byte[] from, byte[] to, int pkey,
+ int padding);
+
+ public static native int RSA_public_decrypt(int flen, byte[] from, byte[] to, int pkey,
+ int padding) throws BadPaddingException, SignatureException;
+
+ public static native int RSA_public_encrypt(int flen, byte[] from, byte[] to, int pkey,
+ int padding);
+
+ public static native int RSA_private_decrypt(int flen, byte[] from, byte[] to, int pkey,
+ int padding) throws BadPaddingException, SignatureException;
+
/**
* @return array of {n, e}
*/
@@ -172,6 +190,8 @@
public static native int RAND_load_file(String filename, long max_bytes);
+ public static native void RAND_bytes(byte[] output);
+
// --- X509_NAME -----------------------------------------------------------
public static int X509_NAME_hash(X500Principal principal) {
@@ -333,6 +353,10 @@
public static final int EVP_PKEY_DH = 28; // NID_dhKeyAgreement
public static final int EVP_PKEY_EC = 408; // NID_X9_62_id_ecPublicKey
+ // RSA padding modes from rsa.h
+ public static final int RSA_PKCS1_PADDING = 1;
+ public static final int RSA_NO_PADDING = 3;
+
// SSL mode from ssl.h
public static final long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L;
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java
new file mode 100644
index 0000000..a11ffa9
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+import libcore.util.EmptyArray;
+
+public abstract class OpenSSLCipherRSA extends CipherSpi {
+ /**
+ * The current OpenSSL key we're operating on.
+ */
+ private OpenSSLKey key;
+
+ /**
+ * Current key type: private or public.
+ */
+ private boolean usingPrivateKey;
+
+ /**
+ * Current cipher mode: encrypting or decrypting.
+ */
+ private boolean encrypting;
+
+ /**
+ * Buffer for operations
+ */
+ private byte[] buffer;
+
+ /**
+ * Current offset in the buffer.
+ */
+ private int bufferOffset;
+
+ /**
+ * Flag that indicates an exception should be thrown when the input is too
+ * large during doFinal.
+ */
+ private boolean inputTooLarge;
+
+ /**
+ * Current padding mode
+ */
+ private int padding = NativeCrypto.RSA_PKCS1_PADDING;
+
+ protected OpenSSLCipherRSA(int padding) {
+ this.padding = padding;
+ }
+
+ @Override
+ protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+ final String modeUpper = mode.toUpperCase();
+ if ("NONE".equals(modeUpper) || "ECB".equals(modeUpper)) {
+ return;
+ }
+
+ throw new NoSuchAlgorithmException("mode not supported: " + mode);
+ }
+
+ @Override
+ protected void engineSetPadding(String padding) throws NoSuchPaddingException {
+ final String paddingUpper = padding.toUpperCase();
+ if ("PKCS1PADDING".equals(paddingUpper)) {
+ this.padding = NativeCrypto.RSA_PKCS1_PADDING;
+ return;
+ }
+ if ("NOPADDING".equals(paddingUpper)) {
+ this.padding = NativeCrypto.RSA_NO_PADDING;
+ return;
+ }
+
+ throw new NoSuchPaddingException("padding not supported: " + padding);
+ }
+
+ @Override
+ protected int engineGetBlockSize() {
+ return 0;
+ }
+
+ @Override
+ protected int engineGetOutputSize(int inputLen) {
+ if (key == null) {
+ throw new IllegalStateException("cipher is not initialized");
+ }
+
+ return buffer.length;
+ }
+
+ @Override
+ protected byte[] engineGetIV() {
+ return null;
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ private void engineInitInternal(int opmode, Key key) throws InvalidKeyException {
+ if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
+ encrypting = true;
+ } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
+ encrypting = false;
+ } else {
+ throw new InvalidParameterException("Unsupported opmode " + opmode);
+ }
+
+ if (key instanceof OpenSSLRSAPrivateKey) {
+ OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) key;
+ usingPrivateKey = true;
+ this.key = rsaPrivateKey.getOpenSSLKey();
+ } else if (key instanceof RSAPrivateCrtKey) {
+ RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) key;
+ usingPrivateKey = true;
+ this.key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
+ } else if (key instanceof RSAPrivateKey) {
+ RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) key;
+ usingPrivateKey = true;
+ this.key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
+ } else if (key instanceof OpenSSLRSAPublicKey) {
+ OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) key;
+ usingPrivateKey = false;
+ this.key = rsaPublicKey.getOpenSSLKey();
+ } else if (key instanceof RSAPublicKey) {
+ RSAPublicKey rsaPublicKey = (RSAPublicKey) key;
+ usingPrivateKey = false;
+ this.key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
+ } else {
+ throw new InvalidKeyException("Need RSA private or public key");
+ }
+
+ buffer = new byte[NativeCrypto.RSA_size(this.key.getPkeyContext())];
+ inputTooLarge = false;
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+ engineInitInternal(opmode, key);
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException("unknown param type: "
+ + params.getClass().getName());
+ }
+
+ engineInitInternal(opmode, key);
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException("unknown param type: "
+ + params.getClass().getName());
+ }
+
+ engineInitInternal(opmode, key);
+ }
+
+ @Override
+ protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+ if (bufferOffset + inputLen > buffer.length) {
+ inputTooLarge = true;
+ return EmptyArray.BYTE;
+ }
+
+ System.arraycopy(input, inputOffset, buffer, bufferOffset, inputLen);
+ bufferOffset += inputLen;
+ return EmptyArray.BYTE;
+ }
+
+ @Override
+ protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset) throws ShortBufferException {
+ engineUpdate(input, inputOffset, inputLen);
+ return 0;
+ }
+
+ @Override
+ protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+ throws IllegalBlockSizeException, BadPaddingException {
+ if (input != null) {
+ engineUpdate(input, inputOffset, inputLen);
+ }
+
+ if (inputTooLarge) {
+ throw new IllegalBlockSizeException("input must be under " + buffer.length + " bytes");
+ }
+
+ final byte[] tmpBuf;
+ if (bufferOffset != buffer.length) {
+ if (padding == NativeCrypto.RSA_NO_PADDING) {
+ tmpBuf = new byte[buffer.length];
+ System.arraycopy(buffer, 0, tmpBuf, buffer.length - bufferOffset, bufferOffset);
+ } else {
+ tmpBuf = Arrays.copyOf(buffer, bufferOffset);
+ }
+ } else {
+ tmpBuf = buffer;
+ }
+
+ byte[] output = new byte[buffer.length];
+ int resultSize;
+ if (encrypting) {
+ if (usingPrivateKey) {
+ resultSize = NativeCrypto.RSA_private_encrypt(tmpBuf.length, tmpBuf, output,
+ key.getPkeyContext(), padding);
+ } else {
+ resultSize = NativeCrypto.RSA_public_encrypt(tmpBuf.length, tmpBuf, output,
+ key.getPkeyContext(), padding);
+ }
+ } else {
+ try {
+ if (usingPrivateKey) {
+ resultSize = NativeCrypto.RSA_private_decrypt(tmpBuf.length, tmpBuf, output,
+ key.getPkeyContext(), padding);
+ } else {
+ resultSize = NativeCrypto.RSA_public_decrypt(tmpBuf.length, tmpBuf, output,
+ key.getPkeyContext(), padding);
+ }
+ } catch (SignatureException e) {
+ IllegalBlockSizeException newE = new IllegalBlockSizeException();
+ newE.initCause(e);
+ throw newE;
+ }
+ }
+ if (!encrypting && resultSize != output.length) {
+ output = Arrays.copyOf(output, resultSize);
+ }
+
+ bufferOffset = 0;
+ return output;
+ }
+
+ @Override
+ protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ byte[] b = engineDoFinal(input, inputOffset, inputLen);
+
+ final int lastOffset = outputOffset + b.length;
+ if (lastOffset > output.length) {
+ throw new ShortBufferException("output buffer is too small " + output.length + " < "
+ + lastOffset);
+ }
+
+ System.arraycopy(b, 0, output, outputOffset, b.length);
+ return b.length;
+ }
+
+ @Override
+ protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException {
+ try {
+ byte[] encoded = key.getEncoded();
+ return engineDoFinal(encoded, 0, encoded.length);
+ } catch (BadPaddingException e) {
+ IllegalBlockSizeException newE = new IllegalBlockSizeException();
+ newE.initCause(e);
+ throw newE;
+ }
+ }
+
+ @Override
+ protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
+ int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
+ try {
+ byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+ if (wrappedKeyType == Cipher.PUBLIC_KEY) {
+ KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+ return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
+ } else if (wrappedKeyType == Cipher.PRIVATE_KEY) {
+ KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+ return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+ } else if (wrappedKeyType == Cipher.SECRET_KEY) {
+ return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+ } else {
+ throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType);
+ }
+ } catch (IllegalBlockSizeException e) {
+ throw new InvalidKeyException(e);
+ } catch (BadPaddingException e) {
+ throw new InvalidKeyException(e);
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
+ public static class PKCS1 extends OpenSSLCipherRSA {
+ public PKCS1() {
+ super(NativeCrypto.RSA_PKCS1_PADDING);
+ }
+ }
+
+ public static class Raw extends OpenSSLCipherRSA {
+ public Raw() {
+ super(NativeCrypto.RSA_NO_PADDING);
+ }
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java
index 7cd16f7..761b08e 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java
@@ -120,6 +120,10 @@
return key.getPkeyContext();
}
+ public String getPkeyAlias() {
+ return key.getAlias();
+ }
+
@Override
public boolean equals(Object o) {
if (o == this) {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java
index e91e6d8..d01dc62 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java
@@ -24,6 +24,8 @@
NativeCrypto.ENGINE_load_dynamic();
}
+ private static final Object mLoadingLock = new Object();
+
/** The ENGINE's native handle. */
private final int ctx;
@@ -32,10 +34,14 @@
throw new NullPointerException("engine == null");
}
- final int engineCtx = NativeCrypto.ENGINE_by_id(engine);
+ final int engineCtx;
+ synchronized (mLoadingLock) {
+ engineCtx = NativeCrypto.ENGINE_by_id(engine);
+ if (engineCtx == 0) {
+ throw new IllegalArgumentException("Unknown ENGINE id: " + engine);
+ }
- if (engineCtx == 0) {
- throw new IllegalArgumentException("Unknown ENGINE id: " + engine);
+ NativeCrypto.ENGINE_add(engineCtx);
}
return new OpenSSLEngine(engineCtx);
@@ -45,6 +51,7 @@
ctx = engineCtx;
if (NativeCrypto.ENGINE_init(engineCtx) == 0) {
+ NativeCrypto.ENGINE_free(engineCtx);
throw new IllegalArgumentException("Could not initialize engine");
}
}
@@ -62,9 +69,9 @@
final int keyType = NativeCrypto.EVP_PKEY_type(keyRef);
switch (keyType) {
case NativeCrypto.EVP_PKEY_RSA:
- return OpenSSLRSAPrivateKey.getInstance(new OpenSSLKey(keyRef, this));
+ return OpenSSLRSAPrivateKey.getInstance(new OpenSSLKey(keyRef, this, id));
case NativeCrypto.EVP_PKEY_DSA:
- return new OpenSSLDSAPrivateKey(new OpenSSLKey(keyRef, this));
+ return new OpenSSLDSAPrivateKey(new OpenSSLKey(keyRef, this, id));
default:
throw new InvalidKeyException("Unknown key type: " + keyType);
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
index 90eb0e2..b8b9f69 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java
@@ -21,14 +21,18 @@
private final OpenSSLEngine engine;
+ private final String alias;
+
OpenSSLKey(int ctx) {
this.ctx = ctx;
engine = null;
+ alias = null;
}
- OpenSSLKey(int ctx, OpenSSLEngine engine) {
+ OpenSSLKey(int ctx, OpenSSLEngine engine, String alias) {
this.ctx = ctx;
this.engine = engine;
+ this.alias = alias;
}
int getPkeyContext() {
@@ -43,6 +47,10 @@
return engine != null;
}
+ String getAlias() {
+ return alias;
+ }
+
@Override
protected void finalize() throws Throwable {
try {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
index 97753cf..e1d0fb0 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
@@ -108,5 +108,22 @@
put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "SHA1withDSA");
put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA");
put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA");
+
+ put("Signature.NONEwithRSA", OpenSSLSignatureRawRSA.class.getName());
+
+ // SecureRandom
+ /*
+ * We have to specify SHA1PRNG because various documentation mentions
+ * that algorithm by name instead of just recommending calling
+ * "new SecureRandom()"
+ */
+ put("SecureRandom.SHA1PRNG", OpenSSLRandom.class.getName());
+ put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
+
+ // Cipher
+ put("Cipher.RSA/ECB/NoPadding", OpenSSLCipherRSA.Raw.class.getName());
+ put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
+ put("Cipher.RSA/ECB/PKCS1Padding", OpenSSLCipherRSA.PKCS1.class.getName());
+ put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
}
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java
index 8376515..48e1494 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java
@@ -201,6 +201,16 @@
if (getOpenSSLKey().equals(other.getOpenSSLKey())) {
return true;
}
+
+ /*
+ * If this key is ENGINE-based, it could be equivalent to another
+ * ENGINE-based key. The modulus may be equal, but that occurrence
+ * should be so improbably low as to never happen.
+ */
+ if (getOpenSSLKey().isEngineBased() || other.getOpenSSLKey().isEngineBased()) {
+ return publicExponent.equals(other.getPublicExponent())
+ && getModulus().equals(other.getModulus());
+ }
}
if (o instanceof RSAPrivateCrtKey) {
@@ -232,11 +242,11 @@
public String toString() {
final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateCrtKey{");
- if (getOpenSSLKey().isEngineBased()) {
+ final boolean engineBased = getOpenSSLKey().isEngineBased();
+ if (engineBased) {
sb.append("key=");
sb.append(getOpenSSLKey());
sb.append('}');
- return sb.toString();
}
ensureReadParams();
@@ -250,9 +260,11 @@
sb.append(',');
}
- sb.append("privateExponent=");
- sb.append(getPrivateExponent().toString(16));
- sb.append(',');
+ if (!engineBased) {
+ sb.append("privateExponent=");
+ sb.append(getPrivateExponent().toString(16));
+ sb.append(',');
+ }
if (primeP != null) {
sb.append("primeP=");
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
index c9fa178..5c2f075 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
@@ -180,6 +180,10 @@
return key.getPkeyContext();
}
+ public String getPkeyAlias() {
+ return key.getAlias();
+ }
+
@Override
public boolean equals(Object o) {
if (o == this) {
@@ -196,6 +200,15 @@
if (key.equals(other.getOpenSSLKey())) {
return true;
}
+
+ /*
+ * If this key is ENGINE-based, it could be equivalent to another
+ * ENGINE-based key. The modulus may be equal, but that occurrence
+ * should be so improbably low as to never happen.
+ */
+ if (key.isEngineBased() || other.getOpenSSLKey().isEngineBased()) {
+ return modulus.equals(other.getModulus());
+ }
}
if (o instanceof RSAPrivateKey) {
@@ -226,11 +239,11 @@
public String toString() {
final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{");
- if (key.isEngineBased()) {
+ final boolean engineBased = key.isEngineBased();
+ if (engineBased) {
sb.append("key=");
sb.append(key);
sb.append('}');
- return sb.toString();
}
ensureReadParams();
@@ -238,9 +251,11 @@
sb.append(modulus.toString(16));
sb.append(',');
- sb.append("privateExponent=");
- sb.append(privateExponent.toString(16));
- sb.append(',');
+ if (!engineBased) {
+ sb.append("privateExponent=");
+ sb.append(privateExponent.toString(16));
+ sb.append(',');
+ }
return sb.toString();
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java
new file mode 100644
index 0000000..fd011f0
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.Serializable;
+import java.security.SecureRandomSpi;
+
+public class OpenSSLRandom extends SecureRandomSpi implements Serializable {
+ private static final long serialVersionUID = 8506210602917522860L;
+
+ @Override
+ protected void engineSetSeed(byte[] seed) {
+ NativeCrypto.RAND_seed(seed);
+ }
+
+ @Override
+ protected void engineNextBytes(byte[] bytes) {
+ NativeCrypto.RAND_bytes(bytes);
+ }
+
+ @Override
+ protected byte[] engineGenerateSeed(int numBytes) {
+ byte[] output = new byte[numBytes];
+ NativeCrypto.RAND_bytes(output);
+ return output;
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
index 35603df..003122f 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
@@ -366,9 +366,7 @@
/**
* Returns the object which is bound to the the input parameter name.
* This name is a sort of link to the data of the SSL session's application
- * layer, if any exists. The search for this link is monitored, as a matter
- * of security, by the full machinery of the <code>AccessController</code>
- * class.
+ * layer, if any exists.
*
* @param name the name of the binding to find.
* @return the value bound to that name, or null if the binding does not
@@ -384,9 +382,7 @@
/**
* Returns an array with the names (sort of links) of all the data
- * objects of the application layer bound into the SSL session. The search
- * for this link is monitored, as a matter of security, by the full
- * machinery of the <code>AccessController</code> class.
+ * objects of the application layer bound into the SSL session.
*
* @return a non-null (possibly empty) array of names of the data objects
* bound to this SSL session.
@@ -399,9 +395,7 @@
* A link (name) with the specified value object of the SSL session's
* application layer data is created or replaced. If the new (or existing)
* value object implements the <code>SSLSessionBindingListener</code>
- * interface, that object will be notified in due course. These links-to
- * -data bounds are monitored, as a matter of security, by the full
- * machinery of the <code>AccessController</code> class.
+ * interface, that object will be notified in due course.
*
* @param name the name of the link (no null are
* accepted!)
@@ -432,10 +426,6 @@
* <p>If the value object implements the <code>SSLSessionBindingListener</code>
* interface, the object will receive a <code>valueUnbound</code> notification.
*
- * <p>These links-to -data bounds are
- * monitored, as a matter of security, by the full machinery of the
- * <code>AccessController</code> class.
- *
* @param name the name of the link (no null are
* accepted!)
* @throws <code>IllegalArgumentException</code> if the argument is null.
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java
new file mode 100644
index 0000000..289af30
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
+
+/**
+ * Implements the JDK Signature interface needed for RAW RSA signature
+ * generation and verification using OpenSSL.
+ */
+public class OpenSSLSignatureRawRSA extends Signature {
+ /**
+ * The current OpenSSL key we're operating on.
+ */
+ private OpenSSLKey key;
+
+ /**
+ * Buffer to hold value to be signed or verified.
+ */
+ private byte[] inputBuffer;
+
+ /**
+ * Current offset in input buffer.
+ */
+ private int inputOffset;
+
+ /**
+ * Provides a flag to specify when the input is too long.
+ */
+ private boolean inputIsTooLong;
+
+ /**
+ * Creates a new OpenSSLSignature instance for the given algorithm name.
+ */
+ public OpenSSLSignatureRawRSA() throws NoSuchAlgorithmException {
+ super("NONEwithRSA");
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ final int oldOffset = inputOffset++;
+
+ if (inputOffset > inputBuffer.length) {
+ inputIsTooLong = true;
+ return;
+ }
+
+ inputBuffer[oldOffset] = input;
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int offset, int len) {
+ final int oldOffset = inputOffset;
+ inputOffset += len;
+
+ if (inputOffset > inputBuffer.length) {
+ inputIsTooLong = true;
+ return;
+ }
+
+ System.arraycopy(input, offset, inputBuffer, oldOffset, len);
+ }
+
+ @Override
+ protected Object engineGetParameter(String param) throws InvalidParameterException {
+ return null;
+ }
+
+ @Override
+ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+ if (privateKey instanceof OpenSSLRSAPrivateKey) {
+ OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey;
+ key = rsaPrivateKey.getOpenSSLKey();
+ } else if (privateKey instanceof RSAPrivateCrtKey) {
+ RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
+ key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
+ } else if (privateKey instanceof RSAPrivateKey) {
+ RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
+ key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
+ } else {
+ throw new InvalidKeyException("Need DSA or RSA private key");
+ }
+
+ // Allocate buffer according to RSA modulus size.
+ int maxSize = NativeCrypto.RSA_size(key.getPkeyContext());
+ inputBuffer = new byte[maxSize];
+ inputOffset = 0;
+ }
+
+ @Override
+ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+ if (publicKey instanceof OpenSSLRSAPublicKey) {
+ OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey;
+ key = rsaPublicKey.getOpenSSLKey();
+ } else if (publicKey instanceof RSAPublicKey) {
+ RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
+ key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
+ } else {
+ throw new InvalidKeyException("Need DSA or RSA public key");
+ }
+
+ // Allocate buffer according to RSA modulus size.
+ int maxSize = NativeCrypto.RSA_size(key.getPkeyContext());
+ inputBuffer = new byte[maxSize];
+ inputOffset = 0;
+ }
+
+ @Override
+ protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+ }
+
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ if (key == null) {
+ // This can't actually happen, but you never know...
+ throw new SignatureException("Need RSA private key");
+ }
+
+ if (inputIsTooLong) {
+ throw new SignatureException("input length " + inputOffset + " != "
+ + inputBuffer.length + " (modulus size)");
+ }
+
+ byte[] outputBuffer = new byte[inputBuffer.length];
+ try {
+ NativeCrypto.RSA_private_encrypt(inputOffset, inputBuffer, outputBuffer,
+ key.getPkeyContext(), NativeCrypto.RSA_PKCS1_PADDING);
+ return outputBuffer;
+ } catch (Exception ex) {
+ throw new SignatureException(ex);
+ } finally {
+ inputOffset = 0;
+ }
+ }
+
+ @Override
+ protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+ if (key == null) {
+ // This can't actually happen, but you never know...
+ throw new SignatureException("Need RSA public key");
+ }
+
+ if (inputIsTooLong) {
+ return false;
+ }
+
+ byte[] outputBuffer = new byte[inputBuffer.length];
+ try {
+ final int resultSize;
+ try {
+ resultSize = NativeCrypto.RSA_public_decrypt(sigBytes.length, sigBytes,
+ outputBuffer, key.getPkeyContext(), NativeCrypto.RSA_PKCS1_PADDING);
+ } catch (SignatureException e) {
+ throw e;
+ } catch (Exception e) {
+ return false;
+ }
+ /* Make this constant time by comparing every byte. */
+ boolean matches = (resultSize == inputOffset);
+ for (int i = 0; i < resultSize; i++) {
+ if (inputBuffer[i] != outputBuffer[i]) {
+ matches = false;
+ }
+ }
+ return matches;
+ } catch (Exception ex) {
+ throw new SignatureException(ex);
+ } finally {
+ inputOffset = 0;
+ }
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
index 42b7500..b258bae 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -861,12 +861,13 @@
}
}
- NativeCrypto.SSL_interrupt(sslNativePointer);
-
synchronized (this) {
+
+ // Interrupt any outstanding reads or writes before taking the writeLock and readLock
+ NativeCrypto.SSL_interrupt(sslNativePointer);
+
synchronized (writeLock) {
synchronized (readLock) {
-
// Shut down the SSL connection, per se.
try {
if (handshakeStarted) {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
index 833f1b7..29cb9cd 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
@@ -19,7 +19,6 @@
import java.io.IOException;
import java.math.BigInteger;
-import java.security.AccessController;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -636,7 +635,7 @@
} else {
if ((parameters.getNeedClientAuth() && clientCert == null)
|| clientKeyExchange == null
- || (clientCert != null
+ || (clientCert != null && clientCert.certs.length > 0
&& !clientKeyExchange.isEmpty()
&& certificateVerify == null)) {
unexpectedMessage();
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java
index 54116a7..abb6e55 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java
@@ -16,6 +16,11 @@
package org.apache.harmony.xnet.provider.jsse;
+import org.apache.harmony.security.x501.Name;
+import org.apache.harmony.security.x509.AuthorityKeyIdentifier;
+import org.apache.harmony.security.x509.GeneralName;
+import org.apache.harmony.security.x509.GeneralNames;
+import org.apache.harmony.security.x509.SubjectKeyIdentifier;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -23,19 +28,20 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.security.KeyStoreSpi;
-import java.security.PublicKey;
-import java.security.cert.CertSelector;
+import java.math.BigInteger;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import libcore.io.IoUtils;
+import libcore.util.Objects;
/**
* A source for trusted root certificate authority (CA) certificates
@@ -366,6 +372,109 @@
return null;
}
+ private static AuthorityKeyIdentifier getAuthorityKeyIdentifier(X509Certificate cert) {
+ final byte[] akidBytes = cert.getExtensionValue("2.5.29.35");
+ if (akidBytes == null) {
+ return null;
+ }
+
+ try {
+ return AuthorityKeyIdentifier.decode(akidBytes);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static SubjectKeyIdentifier getSubjectKeyIdentifier(X509Certificate cert) {
+ final byte[] skidBytes = cert.getExtensionValue("2.5.29.14");
+ if (skidBytes == null) {
+ return null;
+ }
+
+ try {
+ return SubjectKeyIdentifier.decode(skidBytes);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static boolean isSelfSignedCertificate(X509Certificate cert) {
+ if (!Objects.equal(cert.getSubjectX500Principal(), cert.getIssuerX500Principal())) {
+ return false;
+ }
+
+ final AuthorityKeyIdentifier akid = getAuthorityKeyIdentifier(cert);
+ if (akid != null) {
+ final byte[] akidKeyId = akid.getKeyIdentifier();
+ if (akidKeyId != null) {
+ final SubjectKeyIdentifier skid = getSubjectKeyIdentifier(cert);
+ if (!Arrays.equals(akidKeyId, skid.getKeyIdentifier())) {
+ return false;
+ }
+ }
+
+ final BigInteger akidSerial = akid.getAuthorityCertSerialNumber();
+ if (akidSerial != null && !akidSerial.equals(cert.getSerialNumber())) {
+ return false;
+ }
+
+ final GeneralNames possibleIssuerNames = akid.getAuthorityCertIssuer();
+ if (possibleIssuerNames != null) {
+ GeneralName issuerName = null;
+
+ /* Get the first Directory Name (DN) to match how OpenSSL works. */
+ for (GeneralName possibleName : possibleIssuerNames.getNames()) {
+ if (possibleName.getTag() == GeneralName.DIR_NAME) {
+ issuerName = possibleName;
+ break;
+ }
+ }
+
+ if (issuerName != null) {
+ final String issuerCanonical = ((Name) issuerName.getName())
+ .getName(X500Principal.CANONICAL);
+
+ try {
+ final String subjectCanonical = new Name(cert.getSubjectX500Principal()
+ .getEncoded()).getName(X500Principal.CANONICAL);
+ if (!issuerCanonical.equals(subjectCanonical)) {
+ return false;
+ }
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempt to build a certificate chain from the supplied {@code leaf}
+ * argument through the chain of issuers as high up as known. If the chain
+ * can't be completed, the most complete chain available will be returned.
+ * This means that a list with only the {@code leaf} certificate is returned
+ * if no issuer certificates could be found.
+ */
+ public List<X509Certificate> getCertificateChain(X509Certificate leaf) {
+ final List<X509Certificate> chain = new ArrayList<X509Certificate>();
+ chain.add(leaf);
+
+ for (int i = 0; true; i++) {
+ X509Certificate cert = chain.get(i);
+ if (isSelfSignedCertificate(cert)) {
+ break;
+ }
+ X509Certificate issuer = findIssuer(cert);
+ if (issuer == null) {
+ break;
+ }
+ chain.add(issuer);
+ }
+
+ return chain;
+ }
+
// like java.security.cert.CertSelector but with X509Certificate and without cloning
private static interface CertSelector {
public boolean match(X509Certificate cert);
diff --git a/luni/src/main/native/JniException.cpp b/luni/src/main/native/JniException.cpp
index 6626448..c733db4 100644
--- a/luni/src/main/native/JniException.cpp
+++ b/luni/src/main/native/JniException.cpp
@@ -17,22 +17,27 @@
#include "JniException.h"
#include "JNIHelp.h"
-bool maybeThrowIcuException(JNIEnv* env, UErrorCode error) {
+bool maybeThrowIcuException(JNIEnv* env, const char* function, UErrorCode error) {
if (U_SUCCESS(error)) {
return false;
}
- const char* message = u_errorName(error);
+ const char* exceptionClass;
switch (error) {
case U_ILLEGAL_ARGUMENT_ERROR:
- return jniThrowException(env, "java/lang/IllegalArgumentException", message);
+ exceptionClass = "java/lang/IllegalArgumentException";
+ break;
case U_INDEX_OUTOFBOUNDS_ERROR:
case U_BUFFER_OVERFLOW_ERROR:
- return jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", message);
+ exceptionClass = "java/lang/ArrayIndexOutOfBoundsException";
+ break;
case U_UNSUPPORTED_ERROR:
- return jniThrowException(env, "java/lang/UnsupportedOperationException", message);
+ exceptionClass = "java/lang/UnsupportedOperationException";
+ break;
default:
- return jniThrowRuntimeException(env, message);
+ exceptionClass = "java/lang/RuntimeException";
+ break;
}
+ return jniThrowExceptionFmt(env, exceptionClass, "%s failed: %s", function, u_errorName(error));
}
void jniThrowExceptionWithErrno(JNIEnv* env, const char* exceptionClassName, int error) {
diff --git a/luni/src/main/native/JniException.h b/luni/src/main/native/JniException.h
index b3447d2..cece2aa 100644
--- a/luni/src/main/native/JniException.h
+++ b/luni/src/main/native/JniException.h
@@ -25,6 +25,6 @@
void jniThrowOutOfMemoryError(JNIEnv* env, const char* message);
void jniThrowSocketException(JNIEnv* env, int error);
-bool maybeThrowIcuException(JNIEnv* env, UErrorCode error);
+bool maybeThrowIcuException(JNIEnv* env, const char* function, UErrorCode error);
#endif // JNI_EXCEPTION_H_included
diff --git a/luni/src/main/native/java_text_Bidi.cpp b/luni/src/main/native/java_text_Bidi.cpp
index 93c02bb..01bca09 100644
--- a/luni/src/main/native/java_text_Bidi.cpp
+++ b/luni/src/main/native/java_text_Bidi.cpp
@@ -88,18 +88,18 @@
}
UErrorCode err = U_ZERO_ERROR;
ubidi_setPara(data->uBiDi(), chars.get(), length, paraLevel, data->embeddingLevels(), &err);
- maybeThrowIcuException(env, err);
+ maybeThrowIcuException(env, "ubidi_setPara", err);
}
static jlong Bidi_ubidi_setLine(JNIEnv* env, jclass, jlong ptr, jint start, jint limit) {
UErrorCode status = U_ZERO_ERROR;
UBiDi* sized = ubidi_openSized(limit - start, 0, &status);
- if (maybeThrowIcuException(env, status)) {
+ if (maybeThrowIcuException(env, "ubidi_openSized", status)) {
return 0;
}
UniquePtr<BiDiData> lineData(new BiDiData(sized));
ubidi_setLine(uBiDi(ptr), start, limit, lineData->uBiDi(), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ubidi_setLine", status);
return reinterpret_cast<uintptr_t>(lineData.release());
}
@@ -118,7 +118,7 @@
static jbyteArray Bidi_ubidi_getLevels(JNIEnv* env, jclass, jlong ptr) {
UErrorCode status = U_ZERO_ERROR;
const UBiDiLevel* levels = ubidi_getLevels(uBiDi(ptr), &status);
- if (maybeThrowIcuException(env, status)) {
+ if (maybeThrowIcuException(env, "ubidi_getLevels", status)) {
return NULL;
}
int len = ubidi_getLength(uBiDi(ptr));
@@ -130,7 +130,7 @@
static jint Bidi_ubidi_countRuns(JNIEnv* env, jclass, jlong ptr) {
UErrorCode status = U_ZERO_ERROR;
int count = ubidi_countRuns(uBiDi(ptr), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ubidi_countRuns", status);
return count;
}
@@ -141,7 +141,7 @@
UBiDi* ubidi = uBiDi(ptr);
UErrorCode status = U_ZERO_ERROR;
int runCount = ubidi_countRuns(ubidi, &status);
- if (maybeThrowIcuException(env, status)) {
+ if (maybeThrowIcuException(env, "ubidi_countRuns", status)) {
return NULL;
}
jmethodID bidiRunConstructor = env->GetMethodID(JniConstants::bidiRunClass, "<init>", "(III)V");
diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp
index 0f91bd5..6116d2b 100644
--- a/luni/src/main/native/java_util_regex_Matcher.cpp
+++ b/luni/src/main/native/java_util_regex_Matcher.cpp
@@ -71,7 +71,7 @@
if (mJavaInput) {
mEnv->ReleaseStringChars(mJavaInput, mChars);
}
- maybeThrowIcuException(mEnv, mStatus);
+ maybeThrowIcuException(mEnv, "utext_close", mStatus);
}
RegexMatcher* operator->() {
@@ -173,7 +173,7 @@
RegexPattern* pattern = reinterpret_cast<RegexPattern*>(static_cast<uintptr_t>(patternAddr));
UErrorCode status = U_ZERO_ERROR;
RegexMatcher* result = pattern->matcher(status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "RegexPattern::matcher", status);
return static_cast<jint>(reinterpret_cast<uintptr_t>(result));
}
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 5a07694..5224420 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -59,27 +59,37 @@
#include <time.h>
#include <unistd.h>
+// TODO: put this in a header file and use it everywhere!
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
+// It goes in the private: declarations in a class.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
class ScopedResourceBundle {
-public:
- ScopedResourceBundle(UResourceBundle* bundle) : mBundle(bundle) {
+ public:
+ ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) {
+ }
+
+ ~ScopedResourceBundle() {
+ if (bundle_ != NULL) {
+ ures_close(bundle_);
}
+ }
- ~ScopedResourceBundle() {
- if (mBundle != NULL) {
- ures_close(mBundle);
- }
- }
+ UResourceBundle* get() {
+ return bundle_;
+ }
- UResourceBundle* get() {
- return mBundle;
- }
+ bool hasKey(const char* key) {
+ UErrorCode status = U_ZERO_ERROR;
+ ures_getStringByKey(bundle_, key, NULL, &status);
+ return U_SUCCESS(status);
+ }
-private:
- UResourceBundle* mBundle;
-
- // Disallow copy and assignment.
- ScopedResourceBundle(const ScopedResourceBundle&);
- void operator=(const ScopedResourceBundle&);
+ private:
+ UResourceBundle* bundle_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedResourceBundle);
};
Locale getLocale(JNIEnv* env, jstring localeName) {
@@ -302,14 +312,25 @@
}
static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) {
- UErrorCode status = U_ZERO_ERROR;
- int charCount;
- const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status);
- if (U_SUCCESS(status)) {
- setStringField(env, obj, fieldName, env->NewString(chars, charCount));
- } else {
- ALOGE("Error setting String field %s from ICU resource: %s", fieldName, u_errorName(status));
- }
+ UErrorCode status = U_ZERO_ERROR;
+ int charCount;
+ const UChar* chars = ures_getStringByIndex(bundle, index, &charCount, &status);
+ if (U_SUCCESS(status)) {
+ setStringField(env, obj, fieldName, env->NewString(chars, charCount));
+ } else {
+ ALOGE("Error setting String field %s from ICU resource (index %d): %s", fieldName, index, u_errorName(status));
+ }
+}
+
+static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, const char* key) {
+ UErrorCode status = U_ZERO_ERROR;
+ int charCount;
+ const UChar* chars = ures_getStringByKey(bundle, key, &charCount, &status);
+ if (U_SUCCESS(status)) {
+ setStringField(env, obj, fieldName, env->NewString(chars, charCount));
+ } else {
+ ALOGE("Error setting String field %s from ICU resource (key %s): %s", fieldName, key, u_errorName(status));
+ }
}
static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
@@ -361,80 +382,159 @@
setCharField(env, obj, "zeroDigit", dfs.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol));
}
+
+// Iterates up through the locale hierarchy. So "en_US" would return "en_US", "en", "".
+class LocaleNameIterator {
+ public:
+ LocaleNameIterator(const char* locale_name, UErrorCode& status) : status_(status), has_next_(true) {
+ strcpy(locale_name_, locale_name);
+ locale_name_length_ = strlen(locale_name_);
+ }
+
+ const char* Get() {
+ return locale_name_;
+ }
+
+ bool HasNext() {
+ return has_next_;
+ }
+
+ void Up() {
+ locale_name_length_ = uloc_getParent(locale_name_, locale_name_, sizeof(locale_name_), &status_);
+ if (locale_name_length_ == 0) {
+ has_next_ = false;
+ }
+ }
+
+ private:
+ UErrorCode& status_;
+ bool has_next_;
+ char locale_name_[ULOC_FULLNAME_CAPACITY];
+ int32_t locale_name_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocaleNameIterator);
+};
+
+static bool getDateTimePatterns(JNIEnv* env, jobject localeData, const char* locale_name) {
+ UErrorCode status = U_ZERO_ERROR;
+ ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0);
+ setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1);
+ setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2);
+ setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3);
+ setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4);
+ setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5);
+ setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6);
+ setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7);
+ return true;
+}
+
+static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const char* locale_name) {
+ UErrorCode status = U_ZERO_ERROR;
+ ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ ScopedResourceBundle fields(ures_getByKey(gregorian.get(), "fields", NULL, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ ScopedResourceBundle day(ures_getByKey(fields.get(), "day", NULL, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ ScopedResourceBundle relative(ures_getByKey(day.get(), "relative", NULL, &status));
+ if (U_FAILURE(status)) {
+ return false;
+ }
+ // bn_BD only has a "-2" entry.
+ if (relative.hasKey("-1") && relative.hasKey("0") && relative.hasKey("1")) {
+ setStringField(env, localeData, "yesterday", relative.get(), "-1");
+ setStringField(env, localeData, "today", relative.get(), "0");
+ setStringField(env, localeData, "tomorrow", relative.get(), "1");
+ return true;
+ }
+ return false;
+}
+
static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobject localeData) {
ScopedUtfChars localeName(env, locale);
if (localeName.c_str() == NULL) {
return JNI_FALSE;
}
-
- // Get DateTimePatterns
- UErrorCode status;
- char currentLocale[ULOC_FULLNAME_CAPACITY];
- int32_t localeNameLen = 0;
if (localeName.size() >= ULOC_FULLNAME_CAPACITY) {
- return JNI_FALSE; // Exceed ICU defined limit of the whole locale ID.
+ return JNI_FALSE; // ICU has a fixed-length limit.
}
- strcpy(currentLocale, localeName.c_str());
- do {
- status = U_ZERO_ERROR;
- ScopedResourceBundle root(ures_open(NULL, currentLocale, &status));
- if (U_FAILURE(status)) {
- if (localeNameLen == 0) {
- break; // No parent locale, report this error outside the loop.
- } else {
- status = U_ZERO_ERROR;
- continue; // get parent locale.
- }
- }
- ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
- if (U_FAILURE(status)) {
- status = U_ZERO_ERROR;
- continue; // get parent locale.
- }
- ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
- if (U_FAILURE(status)) {
- status = U_ZERO_ERROR;
- continue; // get parent locale.
- }
- ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status));
- if (U_SUCCESS(status)) {
- setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0);
- setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1);
- setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2);
- setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3);
- setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4);
- setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5);
- setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6);
- setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7);
- break;
- } else {
- status = U_ZERO_ERROR; // get parent locale.
- }
- } while((localeNameLen = uloc_getParent(currentLocale, currentLocale, sizeof(currentLocale), &status)) >= 0);
- if (U_FAILURE(status)) {
- ALOGE("Error getting ICU resource bundle: %s", u_errorName(status));
+ // Get the DateTimePatterns.
+ UErrorCode status = U_ZERO_ERROR;
+ bool foundDateTimePatterns = false;
+ for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) {
+ if (getDateTimePatterns(env, localeData, it.Get())) {
+ foundDateTimePatterns = true;
+ break;
+ }
+ }
+ if (!foundDateTimePatterns) {
+ ALOGE("Couldn't find ICU DateTimePatterns for %s", localeName.c_str());
return JNI_FALSE;
}
+ // Get the "Yesterday", "Today", and "Tomorrow" strings.
+ bool foundYesterdayTodayAndTomorrow = false;
+ for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) {
+ if (getYesterdayTodayAndTomorrow(env, localeData, it.Get())) {
+ foundYesterdayTodayAndTomorrow = true;
+ break;
+ }
+ }
+ if (!foundYesterdayTodayAndTomorrow) {
+ ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", localeName.c_str());
+ return JNI_FALSE;
+ }
+
status = U_ZERO_ERROR;
Locale localeObj = getLocale(env, locale);
-
UniquePtr<Calendar> cal(Calendar::createInstance(localeObj, status));
if (U_FAILURE(status)) {
return JNI_FALSE;
}
+
setIntegerField(env, localeData, "firstDayOfWeek", cal->getFirstDayOfWeek());
setIntegerField(env, localeData, "minimalDaysInFirstWeek", cal->getMinimalDaysInFirstWeek());
- // Get DateFormatSymbols
+ // Get DateFormatSymbols.
status = U_ZERO_ERROR;
DateFormatSymbols dateFormatSym(localeObj, status);
if (U_FAILURE(status)) {
return JNI_FALSE;
}
+
+ // Get AM/PM and BC/AD.
int32_t count = 0;
- // Get AM/PM marker
const UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
setStringArrayField(env, localeData, "amPm", amPmStrs, count);
const UnicodeString* erasStrs = dateFormatSym.getEras(count);
@@ -446,12 +546,18 @@
const UnicodeString* shortMonthNames =
dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count);
+ const UnicodeString* tinyMonthNames =
+ dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
+ setStringArrayField(env, localeData, "tinyMonthNames", tinyMonthNames, count);
const UnicodeString* longWeekdayNames =
dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count);
const UnicodeString* shortWeekdayNames =
dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count);
+ const UnicodeString* tinyWeekdayNames =
+ dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
+ setStringArrayField(env, localeData, "tinyWeekdayNames", tinyWeekdayNames, count);
const UnicodeString* longStandAloneMonthNames =
dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
@@ -459,12 +565,18 @@
const UnicodeString* shortStandAloneMonthNames =
dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count);
+ const UnicodeString* tinyStandAloneMonthNames =
+ dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
+ setStringArrayField(env, localeData, "tinyStandAloneMonthNames", tinyStandAloneMonthNames, count);
const UnicodeString* longStandAloneWeekdayNames =
dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count);
const UnicodeString* shortStandAloneWeekdayNames =
dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count);
+ const UnicodeString* tinyStandAloneWeekdayNames =
+ dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
+ setStringArrayField(env, localeData, "tinyStandAloneWeekdayNames", tinyStandAloneWeekdayNames, count);
status = U_ZERO_ERROR;
@@ -543,9 +655,12 @@
UErrorCode status = U_ZERO_ERROR;
UEnumeration* e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
EnumerationCounter counter(uenum_count(e, &status));
+ if (maybeThrowIcuException(env, "uenum_count", status)) {
+ return NULL;
+ }
EnumerationGetter getter(e, &status);
jobject result = toStringArray16(env, &counter, &getter);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "uenum_unext", status);
uenum_close(e);
return result;
}
diff --git a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
index d93c229..5865081 100644
--- a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
+++ b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
@@ -55,14 +55,14 @@
size_t charCount = env->GetStringLength(mString);
UErrorCode status = U_ZERO_ERROR;
ubrk_setText(mIt, mChars, charCount, &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ubrk_setText", status);
}
BreakIteratorPeer* clone(JNIEnv* env) {
UErrorCode status = U_ZERO_ERROR;
jint bufferSize = U_BRK_SAFECLONE_BUFFERSIZE;
UBreakIterator* it = ubrk_safeClone(mIt, NULL, &bufferSize, &status);
- if (maybeThrowIcuException(env, status)) {
+ if (maybeThrowIcuException(env, "ubrk_safeClone", status)) {
return NULL;
}
BreakIteratorPeer* result = new BreakIteratorPeer(it);
@@ -120,7 +120,7 @@
return 0;
}
UBreakIterator* it = ubrk_open(type, localeChars.c_str(), NULL, 0, &status);
- if (maybeThrowIcuException(env, status)) {
+ if (maybeThrowIcuException(env, "ubrk_open", status)) {
return 0;
}
return (new BreakIteratorPeer(it))->toAddress();
diff --git a/luni/src/main/native/libcore_icu_NativeCollation.cpp b/luni/src/main/native/libcore_icu_NativeCollation.cpp
index 3ed49e9..32b1cc4 100644
--- a/luni/src/main/native/libcore_icu_NativeCollation.cpp
+++ b/luni/src/main/native/libcore_icu_NativeCollation.cpp
@@ -50,7 +50,7 @@
static jint NativeCollation_getAttribute(JNIEnv* env, jclass, jint address, jint type) {
UErrorCode status = U_ZERO_ERROR;
jint result = ucol_getAttribute(toCollator(address), (UColAttribute) type, &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_getAttribute", status);
return result;
}
@@ -61,7 +61,7 @@
}
UErrorCode status = U_ZERO_ERROR;
UCollationElements* result = ucol_openElements(toCollator(address), source.get(), source.size(), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_openElements", status);
return static_cast<jint>(reinterpret_cast<uintptr_t>(result));
}
@@ -106,7 +106,7 @@
static jint NativeCollation_next(JNIEnv* env, jclass, jint address) {
UErrorCode status = U_ZERO_ERROR;
jint result = ucol_next(toCollationElements(address), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_next", status);
return result;
}
@@ -117,7 +117,7 @@
}
UErrorCode status = U_ZERO_ERROR;
UCollator* c = ucol_open(localeChars.c_str(), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_open", status);
return static_cast<jint>(reinterpret_cast<uintptr_t>(c));
}
@@ -129,14 +129,14 @@
UErrorCode status = U_ZERO_ERROR;
UCollator* c = ucol_openRules(rules.get(), rules.size(),
UColAttributeValue(mode), UCollationStrength(strength), NULL, &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_openRules", status);
return static_cast<jint>(reinterpret_cast<uintptr_t>(c));
}
static jint NativeCollation_previous(JNIEnv* env, jclass, jint address) {
UErrorCode status = U_ZERO_ERROR;
jint result = ucol_previous(toCollationElements(address), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_previous", status);
return result;
}
@@ -148,20 +148,20 @@
UErrorCode status = U_ZERO_ERROR;
jint bufferSize = U_COL_SAFECLONE_BUFFERSIZE;
UCollator* c = ucol_safeClone(toCollator(address), NULL, &bufferSize, &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_safeClone", status);
return static_cast<jint>(reinterpret_cast<uintptr_t>(c));
}
static void NativeCollation_setAttribute(JNIEnv* env, jclass, jint address, jint type, jint value) {
UErrorCode status = U_ZERO_ERROR;
ucol_setAttribute(toCollator(address), (UColAttribute)type, (UColAttributeValue)value, &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_setAttribute", status);
}
static void NativeCollation_setOffset(JNIEnv* env, jclass, jint address, jint offset) {
UErrorCode status = U_ZERO_ERROR;
ucol_setOffset(toCollationElements(address), offset, &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_setOffset", status);
}
static void NativeCollation_setText(JNIEnv* env, jclass, jint address, jstring javaSource) {
@@ -171,7 +171,7 @@
}
UErrorCode status = U_ZERO_ERROR;
ucol_setText(toCollationElements(address), source.get(), source.size(), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucol_setText", status);
}
static JNINativeMethod gMethods[] = {
diff --git a/luni/src/main/native/libcore_icu_NativeConverter.cpp b/luni/src/main/native/libcore_icu_NativeConverter.cpp
index 5b3761e..6b08ee0 100644
--- a/luni/src/main/native/libcore_icu_NativeConverter.cpp
+++ b/luni/src/main/native/libcore_icu_NativeConverter.cpp
@@ -74,7 +74,7 @@
}
UErrorCode status = U_ZERO_ERROR;
UConverter* cnv = ucnv_open(converterNameChars.c_str(), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "ucnv_open", status);
return reinterpret_cast<uintptr_t>(cnv);
}
@@ -82,24 +82,36 @@
ucnv_close(toUConverter(address));
}
+static bool shouldCodecThrow(jboolean flush, UErrorCode error) {
+ if (flush) {
+ return (error != U_BUFFER_OVERFLOW_ERROR && error != U_TRUNCATED_CHAR_FOUND);
+ } else {
+ return (error != U_BUFFER_OVERFLOW_ERROR && error != U_INVALID_CHAR_FOUND && error != U_ILLEGAL_CHAR_FOUND);
+ }
+}
+
static jint NativeConverter_encode(JNIEnv* env, jclass, jlong address,
jcharArray source, jint sourceEnd, jbyteArray target, jint targetEnd,
jintArray data, jboolean flush) {
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
+ maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedCharArrayRO uSource(env, source);
if (uSource.get() == NULL) {
+ maybeThrowIcuException(env, "uSource", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedByteArrayRW uTarget(env, target);
if (uTarget.get() == NULL) {
+ maybeThrowIcuException(env, "uTarget", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedIntArrayRW myData(env, data);
if (myData.get() == NULL) {
+ maybeThrowIcuException(env, "myData", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
@@ -125,6 +137,11 @@
myData[2] = invalidUCharCount;
}
}
+
+ // Managed code handles some cases; throw all other errors.
+ if (shouldCodecThrow(flush, errorCode)) {
+ maybeThrowIcuException(env, "ucnv_fromUnicode", errorCode);
+ }
return errorCode;
}
@@ -134,18 +151,22 @@
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
+ maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedByteArrayRO uSource(env, source);
if (uSource.get() == NULL) {
+ maybeThrowIcuException(env, "uSource", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedCharArrayRW uTarget(env, target);
if (uTarget.get() == NULL) {
+ maybeThrowIcuException(env, "uTarget", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedIntArrayRW myData(env, data);
if (myData.get() == NULL) {
+ maybeThrowIcuException(env, "myData", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
@@ -172,6 +193,10 @@
}
}
+ // Managed code handles some cases; throw all other errors.
+ if (shouldCodecThrow(flush, errorCode)) {
+ maybeThrowIcuException(env, "ucnv_toUnicode", errorCode);
+ }
return errorCode;
}
@@ -387,11 +412,12 @@
abort();
}
-static jint NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address,
+static void NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address,
jint onMalformedInput, jint onUnmappableInput, jbyteArray javaReplacement) {
UConverter* cnv = toUConverter(address);
- if (!cnv) {
- return U_ILLEGAL_ARGUMENT_ERROR;
+ if (cnv == NULL) {
+ maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
+ return;
}
UConverterFromUCallback oldCallback = NULL;
@@ -409,14 +435,15 @@
ScopedByteArrayRO replacementBytes(env, javaReplacement);
if (replacementBytes.get() == NULL) {
- return U_ILLEGAL_ARGUMENT_ERROR;
+ maybeThrowIcuException(env, "replacementBytes", U_ILLEGAL_ARGUMENT_ERROR);
+ return;
}
memcpy(callbackContext->replacementBytes, replacementBytes.get(), replacementBytes.size());
callbackContext->replacementByteCount = replacementBytes.size();
UErrorCode errorCode = U_ZERO_ERROR;
ucnv_setFromUCallBack(cnv, CHARSET_ENCODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
- return errorCode;
+ maybeThrowIcuException(env, "ucnv_setFromUCallBack", errorCode);
}
static void decoderIgnoreCallback(const void*, UConverterToUnicodeArgs*, const char*, int32_t, UConverterCallbackReason, UErrorCode* err) {
@@ -469,11 +496,12 @@
}
}
-static jint NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address,
+static void NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address,
jint onMalformedInput, jint onUnmappableInput, jstring javaReplacement) {
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
- return U_ILLEGAL_ARGUMENT_ERROR;
+ maybeThrowIcuException(env, "toConverter", U_ILLEGAL_ARGUMENT_ERROR);
+ return;
}
UConverterToUCallback oldCallback;
@@ -491,14 +519,15 @@
ScopedStringChars replacement(env, javaReplacement);
if (replacement.get() == NULL) {
- return U_ILLEGAL_ARGUMENT_ERROR;
+ maybeThrowIcuException(env, "replacement", U_ILLEGAL_ARGUMENT_ERROR);
+ return;
}
u_strncpy(callbackContext->replacementChars, replacement.get(), replacement.size());
callbackContext->replacementCharCount = replacement.size();
UErrorCode errorCode = U_ZERO_ERROR;
ucnv_setToUCallBack(cnv, CHARSET_DECODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
- return errorCode;
+ maybeThrowIcuException(env, "ucnv_setToUCallBack", errorCode);
}
static jfloat NativeConverter_getAveCharsPerByte(JNIEnv* env, jclass, jlong handle) {
@@ -605,8 +634,8 @@
NATIVE_METHOD(NativeConverter, openConverter, "(Ljava/lang/String;)J"),
NATIVE_METHOD(NativeConverter, resetByteToChar, "(J)V"),
NATIVE_METHOD(NativeConverter, resetCharToByte, "(J)V"),
- NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)I"),
- NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)I"),
+ NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)V"),
+ NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)V"),
};
void register_libcore_icu_NativeConverter(JNIEnv* env) {
jniRegisterNativeMethods(env, "libcore/icu/NativeConverter", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
index 1fe4055..0406094 100644
--- a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
@@ -122,7 +122,7 @@
if (fmt == NULL) {
delete symbols;
}
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "DecimalFormat::DecimalFormat", status);
return static_cast<jint>(reinterpret_cast<uintptr_t>(fmt));
}
@@ -144,7 +144,7 @@
UErrorCode status = U_ZERO_ERROR;
UNumberFormatSymbol symbol = static_cast<UNumberFormatSymbol>(javaSymbol);
unum_setSymbol(toUNumberFormat(addr), symbol, value.get(), value.size(), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "unum_setSymbol", status);
}
static void NativeDecimalFormat_setAttribute(JNIEnv*, jclass, jint addr, jint javaAttr, jint value) {
@@ -165,7 +165,7 @@
UErrorCode status = U_ZERO_ERROR;
UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute>(javaAttr);
unum_setTextAttribute(toUNumberFormat(addr), attr, value.get(), value.size(), &status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "unum_setTextAttribute", status);
}
static jstring NativeDecimalFormat_getTextAttribute(JNIEnv* env, jclass, jint addr, jint javaAttr) {
@@ -184,7 +184,7 @@
chars.reset(new UChar[charCount]);
charCount = unum_getTextAttribute(fmt, attr, chars.get(), charCount, &status);
}
- return maybeThrowIcuException(env, status) ? NULL : env->NewString(chars.get(), charCount);
+ return maybeThrowIcuException(env, "unum_getTextAttribute", status) ? NULL : env->NewString(chars.get(), charCount);
}
static void NativeDecimalFormat_applyPatternImpl(JNIEnv* env, jclass, jint addr, jboolean localized, jstring pattern0) {
@@ -195,12 +195,15 @@
ScopedJavaUnicodeString pattern(env, pattern0);
DecimalFormat* fmt = toDecimalFormat(addr);
UErrorCode status = U_ZERO_ERROR;
+ const char* function;
if (localized) {
+ function = "DecimalFormat::applyLocalizedPattern";
fmt->applyLocalizedPattern(pattern.unicodeString(), status);
} else {
+ function = "DecimalFormat::applyPattern";
fmt->applyPattern(pattern.unicodeString(), status);
}
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, function, status);
}
static jstring NativeDecimalFormat_toPatternImpl(JNIEnv* env, jclass, jint addr, jboolean localized) {
@@ -300,9 +303,9 @@
fmt->parse(src.unicodeString(), res, pp);
if (pp.getErrorIndex() == -1) {
- env->CallVoidMethod(position, gPP_setIndex, (jint) pp.getIndex());
+ env->CallVoidMethod(position, gPP_setIndex, pp.getIndex());
} else {
- env->CallVoidMethod(position, gPP_setErrorIndex, (jint) pp.getErrorIndex());
+ env->CallVoidMethod(position, gPP_setErrorIndex, pp.getErrorIndex());
return NULL;
}
@@ -316,30 +319,18 @@
strncmp(data, "Inf", 3) == 0 ||
strncmp(data, "-Inf", 4) == 0) {
double resultDouble = res.getDouble(status);
- return doubleValueOf(env, (jdouble) resultDouble);
+ return doubleValueOf(env, resultDouble);
}
return newBigDecimal(env, data, len);
}
return NULL;
}
- Formattable::Type numType = res.getType();
- switch(numType) {
- case Formattable::kDouble: {
- double resultDouble = res.getDouble();
- return doubleValueOf(env, (jdouble) resultDouble);
- }
- case Formattable::kLong: {
- long resultLong = res.getLong();
- return longValueOf(env, (jlong) resultLong);
- }
- case Formattable::kInt64: {
- int64_t resultInt64 = res.getInt64();
- return longValueOf(env, (jlong) resultInt64);
- }
- default: {
- return NULL;
- }
+ switch (res.getType()) {
+ case Formattable::kDouble: return doubleValueOf(env, res.getDouble());
+ case Formattable::kLong: return longValueOf(env, res.getLong());
+ case Formattable::kInt64: return longValueOf(env, res.getInt64());
+ default: return NULL;
}
}
diff --git a/luni/src/main/native/libcore_icu_NativeNormalizer.cpp b/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
index 58f460c..693e944 100644
--- a/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
+++ b/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
@@ -28,7 +28,7 @@
UErrorCode status = U_ZERO_ERROR;
UnicodeString dst;
Normalizer::normalize(src.unicodeString(), mode, 0, dst, status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "Normalizer::normalize", status);
return dst.isBogus() ? NULL : env->NewString(dst.getBuffer(), dst.length());
}
@@ -37,7 +37,7 @@
UNormalizationMode mode = static_cast<UNormalizationMode>(intMode);
UErrorCode status = U_ZERO_ERROR;
UBool result = Normalizer::isNormalized(src.unicodeString(), mode, status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "Normalizer::isNormalized", status);
return result;
}
diff --git a/luni/src/main/native/libcore_icu_NativePluralRules.cpp b/luni/src/main/native/libcore_icu_NativePluralRules.cpp
index a7164d0..df228ff 100644
--- a/luni/src/main/native/libcore_icu_NativePluralRules.cpp
+++ b/luni/src/main/native/libcore_icu_NativePluralRules.cpp
@@ -34,7 +34,7 @@
Locale locale = Locale::createFromName(ScopedUtfChars(env, localeName).c_str());
UErrorCode status = U_ZERO_ERROR;
PluralRules* result = PluralRules::forLocale(locale, status);
- maybeThrowIcuException(env, status);
+ maybeThrowIcuException(env, "PluralRules::forLocale", status);
return reinterpret_cast<uintptr_t>(result);
}
diff --git a/luni/src/main/native/libcore_icu_TimeZones.cpp b/luni/src/main/native/libcore_icu_TimeZones.cpp
index b543238..15c18c5 100644
--- a/luni/src/main/native/libcore_icu_TimeZones.cpp
+++ b/luni/src/main/native/libcore_icu_TimeZones.cpp
@@ -43,14 +43,14 @@
}
UErrorCode status = U_ZERO_ERROR;
int32_t idCount = ids->count(status);
- if (maybeThrowIcuException(env, status)) {
+ if (maybeThrowIcuException(env, "StringEnumeration::count", status)) {
return NULL;
}
jobjectArray result = env->NewObjectArray(idCount, JniConstants::stringClass, NULL);
for (int32_t i = 0; i < idCount; ++i) {
const UnicodeString* id = ids->snext(status);
- if (maybeThrowIcuException(env, status)) {
+ if (maybeThrowIcuException(env, "StringEnumeration::snext", status)) {
return NULL;
}
ScopedLocalRef<jstring> idString(env, env->NewString(id->getBuffer(), id->length()));
diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp
index 4f1115f..fe5f12d 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -31,49 +31,114 @@
#if defined(__arm__)
// 32-bit ARM has load/store alignment restrictions for longs.
#define LONG_ALIGNMENT_MASK 0x3
+#define INT_ALIGNMENT_MASK 0x0
+#define SHORT_ALIGNMENT_MASK 0x0
+#elif defined(__mips__)
+// MIPS has load/store alignment restrictions for longs, ints and shorts.
+#define LONG_ALIGNMENT_MASK 0x7
+#define INT_ALIGNMENT_MASK 0x3
+#define SHORT_ALIGNMENT_MASK 0x1
#elif defined(__i386__)
// x86 can load anything at any alignment.
#define LONG_ALIGNMENT_MASK 0x0
+#define INT_ALIGNMENT_MASK 0x0
+#define SHORT_ALIGNMENT_MASK 0x0
#else
#error unknown load/store alignment restrictions for this architecture
#endif
+// Use packed structures for access to unaligned data on targets with alignment restrictions.
+// The compiler will generate appropriate code to access these structures without
+// generating alignment exceptions.
+template <typename T> static inline T get_unaligned(const T* address) {
+ struct unaligned { T v; } __attribute__ ((packed));
+ const unaligned* p = reinterpret_cast<const unaligned*>(address);
+ return p->v;
+}
+
+template <typename T> static inline void put_unaligned(T* address, T v) {
+ struct unaligned { T v; } __attribute__ ((packed));
+ unaligned* p = reinterpret_cast<unaligned*>(address);
+ p->v = v;
+}
+
template <typename T> static T cast(jint address) {
return reinterpret_cast<T>(static_cast<uintptr_t>(address));
}
+// Byte-swap 2 jshort values packed in a jint.
+static inline jint bswap_2x16(jint v) {
+ // v is initially ABCD
+#if defined(__mips__) && defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
+ __asm__ volatile ("wsbh %0, %0" : "+r" (v)); // v=BADC
+#else
+ v = bswap_32(v); // v=DCBA
+ v = (v << 16) | ((v >> 16) & 0xffff); // v=BADC
+#endif
+ return v;
+}
+
static inline void swapShorts(jshort* dstShorts, const jshort* srcShorts, size_t count) {
// Do 32-bit swaps as long as possible...
jint* dst = reinterpret_cast<jint*>(dstShorts);
const jint* src = reinterpret_cast<const jint*>(srcShorts);
- for (size_t i = 0; i < count / 2; ++i) {
- jint v = *src++; // v=ABCD
- v = bswap_32(v); // v=DCBA
- jint v2 = (v << 16) | ((v >> 16) & 0xffff); // v=BADC
- *dst++ = v2;
- }
- // ...with one last 16-bit swap if necessary.
- if ((count % 2) != 0) {
- jshort v = *reinterpret_cast<const jshort*>(src);
- *reinterpret_cast<jshort*>(dst) = bswap_16(v);
+
+ if ((reinterpret_cast<uintptr_t>(dst) & INT_ALIGNMENT_MASK) == 0 &&
+ (reinterpret_cast<uintptr_t>(src) & INT_ALIGNMENT_MASK) == 0) {
+ for (size_t i = 0; i < count / 2; ++i) {
+ jint v = *src++;
+ *dst++ = bswap_2x16(v);
+ }
+ // ...with one last 16-bit swap if necessary.
+ if ((count % 2) != 0) {
+ jshort v = *reinterpret_cast<const jshort*>(src);
+ *reinterpret_cast<jshort*>(dst) = bswap_16(v);
+ }
+ } else {
+ for (size_t i = 0; i < count / 2; ++i) {
+ jint v = get_unaligned<jint>(src++);
+ put_unaligned<jint>(dst++, bswap_2x16(v));
+ }
+ if ((count % 2) != 0) {
+ jshort v = get_unaligned<jshort>(reinterpret_cast<const jshort*>(src));
+ put_unaligned<jshort>(reinterpret_cast<jshort*>(dst), bswap_16(v));
+ }
}
}
static inline void swapInts(jint* dstInts, const jint* srcInts, size_t count) {
- for (size_t i = 0; i < count; ++i) {
- jint v = *srcInts++;
- *dstInts++ = bswap_32(v);
+ if ((reinterpret_cast<uintptr_t>(dstInts) & INT_ALIGNMENT_MASK) == 0 &&
+ (reinterpret_cast<uintptr_t>(srcInts) & INT_ALIGNMENT_MASK) == 0) {
+ for (size_t i = 0; i < count; ++i) {
+ jint v = *srcInts++;
+ *dstInts++ = bswap_32(v);
+ }
+ } else {
+ for (size_t i = 0; i < count; ++i) {
+ jint v = get_unaligned<int>(srcInts++);
+ put_unaligned<jint>(dstInts++, bswap_32(v));
+ }
}
}
static inline void swapLongs(jlong* dstLongs, const jlong* srcLongs, size_t count) {
jint* dst = reinterpret_cast<jint*>(dstLongs);
const jint* src = reinterpret_cast<const jint*>(srcLongs);
- for (size_t i = 0; i < count; ++i) {
- jint v1 = *src++;
- jint v2 = *src++;
- *dst++ = bswap_32(v2);
- *dst++ = bswap_32(v1);
+ if ((reinterpret_cast<uintptr_t>(dstLongs) & INT_ALIGNMENT_MASK) == 0 &&
+ (reinterpret_cast<uintptr_t>(srcLongs) & INT_ALIGNMENT_MASK) == 0) {
+ for (size_t i = 0; i < count; ++i) {
+ jint v1 = *src++;
+ jint v2 = *src++;
+ *dst++ = bswap_32(v2);
+ *dst++ = bswap_32(v1);
+ }
+ } else {
+ for (size_t i = 0; i < count; ++i) {
+ jint v1 = get_unaligned<jint>(src++);
+ jint v2 = get_unaligned<jint>(src++);
+ put_unaligned<jint>(dst++, bswap_32(v2));
+ put_unaligned<jint>(dst++, bswap_32(v1));
+ }
}
}
@@ -226,20 +291,11 @@
static jlong Memory_peekLong(JNIEnv*, jclass, jint srcAddress, jboolean swap) {
jlong result;
+ const jlong* src = cast<const jlong*>(srcAddress);
if ((srcAddress & LONG_ALIGNMENT_MASK) == 0) {
- result = *cast<const jlong*>(srcAddress);
+ result = *src;
} else {
- // Handle unaligned memory access one byte at a time
- const jbyte* src = cast<const jbyte*>(srcAddress);
- jbyte* dst = reinterpret_cast<jbyte*>(&result);
- dst[0] = src[0];
- dst[1] = src[1];
- dst[2] = src[2];
- dst[3] = src[3];
- dst[4] = src[4];
- dst[5] = src[5];
- dst[6] = src[6];
- dst[7] = src[7];
+ result = get_unaligned<jlong>(src);
}
if (swap) {
result = bswap_64(result);
@@ -248,23 +304,14 @@
}
static void Memory_pokeLong(JNIEnv*, jclass, jint dstAddress, jlong value, jboolean swap) {
+ jlong* dst = cast<jlong*>(dstAddress);
if (swap) {
value = bswap_64(value);
}
if ((dstAddress & LONG_ALIGNMENT_MASK) == 0) {
- *cast<jlong*>(dstAddress) = value;
+ *dst = value;
} else {
- // Handle unaligned memory access one byte at a time
- const jbyte* src = reinterpret_cast<const jbyte*>(&value);
- jbyte* dst = cast<jbyte*>(dstAddress);
- dst[0] = src[0];
- dst[1] = src[1];
- dst[2] = src[2];
- dst[3] = src[3];
- dst[4] = src[4];
- dst[5] = src[5];
- dst[6] = src[6];
- dst[7] = src[7];
+ put_unaligned<jlong>(dst, value);
}
}
diff --git a/luni/src/main/native/libcore_io_OsConstants.cpp b/luni/src/main/native/libcore_io_OsConstants.cpp
index 622ce6d..4a719af 100644
--- a/luni/src/main/native/libcore_io_OsConstants.cpp
+++ b/luni/src/main/native/libcore_io_OsConstants.cpp
@@ -230,6 +230,7 @@
initConstant(env, c, "O_CREAT", O_CREAT);
initConstant(env, c, "O_EXCL", O_EXCL);
initConstant(env, c, "O_NOCTTY", O_NOCTTY);
+ initConstant(env, c, "O_NOFOLLOW", O_NOFOLLOW);
initConstant(env, c, "O_NONBLOCK", O_NONBLOCK);
initConstant(env, c, "O_RDONLY", O_RDONLY);
initConstant(env, c, "O_RDWR", O_RDWR);
@@ -275,7 +276,9 @@
initConstant(env, c, "SIGRTMAX", SIGRTMAX);
initConstant(env, c, "SIGRTMIN", SIGRTMIN);
initConstant(env, c, "SIGSEGV", SIGSEGV);
+#if defined(SIGSTKFLT)
initConstant(env, c, "SIGSTKFLT", SIGSTKFLT);
+#endif
initConstant(env, c, "SIGSTOP", SIGSTOP);
initConstant(env, c, "SIGSYS", SIGSYS);
initConstant(env, c, "SIGTERM", SIGTERM);
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 30ca145..dd9b87f 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -52,6 +52,7 @@
#include <sys/utsname.h>
#include <sys/vfs.h> // Bionic doesn't have <sys/statvfs.h>
#include <sys/wait.h>
+#include <termios.h>
#include <unistd.h>
#define TO_JAVA_STRING(NAME, EXP) \
@@ -67,36 +68,27 @@
};
/**
- * Used to retry syscalls that can return EINTR. This differs from TEMP_FAILURE_RETRY in that
- * it also considers the case where the reason for failure is that another thread called
- * Socket.close.
- *
- * Assumes 'JNIEnv* env' and 'jobject javaFd' (which is a java.io.FileDescriptor) are in scope.
+ * Used to retry networking system calls that can return EINTR. Unlike TEMP_FAILURE_RETRY,
+ * this also handles the case where the reason for failure is that another thread called
+ * Socket.close. This macro also throws exceptions on failure.
*
* Returns the result of 'exp', though a Java exception will be pending if the result is -1.
- *
- * Correct usage looks like this:
- *
- * void Posix_syscall(JNIEnv* env, jobject javaFd, ...) {
- * ...
- * int fd;
- * NET_FAILURE_RETRY("syscall", syscall(fd, ...)); // Throws on error.
- * }
*/
-#define NET_FAILURE_RETRY(syscall_name, exp) ({ \
- typeof (exp) _rc = -1; \
+#define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
+ return_type _rc = -1; \
do { \
{ \
- fd = jniGetFDFromFileDescriptor(env, javaFd); \
- AsynchronousSocketCloseMonitor monitor(fd); \
- _rc = (exp); \
+ int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
+ AsynchronousSocketCloseMonitor _monitor(_fd); \
+ _rc = syscall_name(_fd, __VA_ARGS__); \
} \
if (_rc == -1) { \
- if (jniGetFDFromFileDescriptor(env, javaFd) == -1) { \
- jniThrowException(env, "java/net/SocketException", "Socket closed"); \
+ if (jniGetFDFromFileDescriptor(jni_env, java_fd) == -1) { \
+ jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
break; \
} else if (errno != EINTR) { \
- throwErrnoException(env, syscall_name); \
+ /* TODO: with a format string we could show the arguments too, like strace(1). */ \
+ throwErrnoException(jni_env, # syscall_name); \
break; \
} \
} \
@@ -379,10 +371,9 @@
sockaddr_storage ss;
socklen_t sl = sizeof(ss);
memset(&ss, 0, sizeof(ss));
- int fd;
sockaddr* peer = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
socklen_t* peerLength = (javaInetSocketAddress != NULL) ? &sl : 0;
- jint clientFd = NET_FAILURE_RETRY("accept", accept(fd, peer, peerLength));
+ jint clientFd = NET_FAILURE_RETRY(env, int, accept, javaFd, peer, peerLength);
if (clientFd == -1 || !fillInetSocketAddress(env, clientFd, javaInetSocketAddress, &ss)) {
close(clientFd);
return NULL;
@@ -407,9 +398,9 @@
if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) {
return;
}
- int fd;
const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
- NET_FAILURE_RETRY("bind", bind(fd, sa, sizeof(sockaddr_storage)));
+ // We don't need the return value because we'll already have thrown.
+ (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sizeof(sockaddr_storage));
}
static void Posix_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) {
@@ -420,6 +411,14 @@
throwIfMinusOne(env, "chmod", TEMP_FAILURE_RETRY(chmod(path.c_str(), mode)));
}
+static void Posix_chown(JNIEnv* env, jobject, jstring javaPath, jint uid, jint gid) {
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return;
+ }
+ throwIfMinusOne(env, "chown", TEMP_FAILURE_RETRY(chown(path.c_str(), uid, gid)));
+}
+
static void Posix_close(JNIEnv* env, jobject, jobject javaFd) {
// Get the FileDescriptor's 'fd' field and clear it.
// We need to do this before we can throw an IOException (http://b/3222087).
@@ -437,9 +436,9 @@
if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) {
return;
}
- int fd;
const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
- NET_FAILURE_RETRY("connect", connect(fd, sa, sizeof(sockaddr_storage)));
+ // We don't need the return value because we'll already have thrown.
+ (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sizeof(sockaddr_storage));
}
static jobject Posix_dup(JNIEnv* env, jobject, jobject javaOldFd) {
@@ -459,6 +458,16 @@
return toStringArray(env, environ);
}
+static void Posix_fchmod(JNIEnv* env, jobject, jobject javaFd, jint mode) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ throwIfMinusOne(env, "fchmod", TEMP_FAILURE_RETRY(fchmod(fd, mode)));
+}
+
+static void Posix_fchown(JNIEnv* env, jobject, jobject javaFd, jint uid, jint gid) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ throwIfMinusOne(env, "fchown", TEMP_FAILURE_RETRY(fchown(fd, uid, gid)));
+}
+
static jint Posix_fcntlVoid(JNIEnv* env, jobject, jobject javaFd, jint cmd) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd)));
@@ -791,13 +800,21 @@
static jboolean Posix_isatty(JNIEnv* env, jobject, jobject javaFd) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return TEMP_FAILURE_RETRY(isatty(fd)) == 0;
+ return TEMP_FAILURE_RETRY(isatty(fd)) == 1;
}
static void Posix_kill(JNIEnv* env, jobject, jint pid, jint sig) {
throwIfMinusOne(env, "kill", TEMP_FAILURE_RETRY(kill(pid, sig)));
}
+static void Posix_lchown(JNIEnv* env, jobject, jstring javaPath, jint uid, jint gid) {
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return;
+ }
+ throwIfMinusOne(env, "lchown", TEMP_FAILURE_RETRY(lchown(path.c_str(), uid, gid)));
+}
+
static void Posix_listen(JNIEnv* env, jobject, jobject javaFd, jint backlog) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
throwIfMinusOne(env, "listen", TEMP_FAILURE_RETRY(listen(fd, backlog)));
@@ -983,10 +1000,9 @@
sockaddr_storage ss;
socklen_t sl = sizeof(ss);
memset(&ss, 0, sizeof(ss));
- int fd;
sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0;
- jint recvCount = NET_FAILURE_RETRY("recvfrom", recvfrom(fd, bytes.get() + byteOffset, byteCount, flags, from, fromLength));
+ jint recvCount = NET_FAILURE_RETRY(env, ssize_t, recvfrom, javaFd, bytes.get() + byteOffset, byteCount, flags, from, fromLength);
fillInetSocketAddress(env, recvCount, javaInetSocketAddress, &ss);
return recvCount;
}
@@ -1038,10 +1054,9 @@
if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, &ss)) {
return -1;
}
- int fd;
const sockaddr* to = (javaInetAddress != NULL) ? reinterpret_cast<const sockaddr*>(&ss) : NULL;
socklen_t toLength = (javaInetAddress != NULL) ? sizeof(ss) : 0;
- return NET_FAILURE_RETRY("sendto", sendto(fd, bytes.get() + byteOffset, byteCount, flags, to, toLength));
+ return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, to, toLength);
}
static void Posix_setegid(JNIEnv* env, jobject, jint egid) {
@@ -1056,6 +1071,10 @@
throwIfMinusOne(env, "setgid", TEMP_FAILURE_RETRY(setgid(gid)));
}
+static jint Posix_setsid(JNIEnv* env, jobject) {
+ return throwIfMinusOne(env, "setsid", TEMP_FAILURE_RETRY(setsid()));
+}
+
static void Posix_setsockoptByte(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
u_char byte = value;
@@ -1102,7 +1121,7 @@
if (rc == -1 && errno == EINVAL) {
// Maybe we're a 32-bit binary talking to a 64-bit kernel?
// glibc doesn't automatically handle this.
- struct GCC_HIDDEN group_req64 {
+ struct group_req64 {
uint32_t gr_interface;
uint32_t my_padding;
sockaddr_storage gr_group;
@@ -1195,6 +1214,15 @@
return result;
}
+static void Posix_tcdrain(JNIEnv* env, jobject, jobject javaFd) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ throwIfMinusOne(env, "tcdrain", TEMP_FAILURE_RETRY(tcdrain(fd)));
+}
+
+static jint Posix_umask(JNIEnv*, jobject, jint mask) {
+ return umask(mask);
+}
+
static jobject Posix_uname(JNIEnv* env, jobject) {
struct utsname buf;
if (TEMP_FAILURE_RETRY(uname(&buf)) == -1) {
@@ -1236,11 +1264,14 @@
NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"),
+ NATIVE_METHOD(Posix, chown, "(Ljava/lang/String;II)V"),
NATIVE_METHOD(Posix, close, "(Ljava/io/FileDescriptor;)V"),
NATIVE_METHOD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
NATIVE_METHOD(Posix, dup, "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, dup2, "(Ljava/io/FileDescriptor;I)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, environ, "()[Ljava/lang/String;"),
+ NATIVE_METHOD(Posix, fchmod, "(Ljava/io/FileDescriptor;I)V"),
+ NATIVE_METHOD(Posix, fchown, "(Ljava/io/FileDescriptor;II)V"),
NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"),
NATIVE_METHOD(Posix, fcntlLong, "(Ljava/io/FileDescriptor;IJ)I"),
NATIVE_METHOD(Posix, fcntlFlock, "(Ljava/io/FileDescriptor;ILlibcore/io/StructFlock;)I"),
@@ -1273,6 +1304,7 @@
NATIVE_METHOD(Posix, ioctlInt, "(Ljava/io/FileDescriptor;ILlibcore/util/MutableInt;)I"),
NATIVE_METHOD(Posix, isatty, "(Ljava/io/FileDescriptor;)Z"),
NATIVE_METHOD(Posix, kill, "(II)V"),
+ NATIVE_METHOD(Posix, lchown, "(Ljava/lang/String;II)V"),
NATIVE_METHOD(Posix, listen, "(Ljava/io/FileDescriptor;I)V"),
NATIVE_METHOD(Posix, lseek, "(Ljava/io/FileDescriptor;JI)J"),
NATIVE_METHOD(Posix, lstat, "(Ljava/lang/String;)Llibcore/io/StructStat;"),
@@ -1298,6 +1330,7 @@
NATIVE_METHOD(Posix, setegid, "(I)V"),
NATIVE_METHOD(Posix, seteuid, "(I)V"),
NATIVE_METHOD(Posix, setgid, "(I)V"),
+ NATIVE_METHOD(Posix, setsid, "()I"),
NATIVE_METHOD(Posix, setsockoptByte, "(Ljava/io/FileDescriptor;III)V"),
NATIVE_METHOD(Posix, setsockoptIfreq, "(Ljava/io/FileDescriptor;IILjava/lang/String;)V"),
NATIVE_METHOD(Posix, setsockoptInt, "(Ljava/io/FileDescriptor;III)V"),
@@ -1313,6 +1346,8 @@
NATIVE_METHOD(Posix, strerror, "(I)Ljava/lang/String;"),
NATIVE_METHOD(Posix, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, sysconf, "(I)J"),
+ NATIVE_METHOD(Posix, tcdrain, "(Ljava/io/FileDescriptor;)V"),
+ NATIVE_METHOD(Posix, umask, "(I)I"),
NATIVE_METHOD(Posix, uname, "()Llibcore/io/StructUtsname;"),
NATIVE_METHOD(Posix, waitpid, "(ILlibcore/util/MutableInt;I)I"),
NATIVE_METHOD(Posix, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),
diff --git a/luni/src/main/native/libcore_net_RawSocket.cpp b/luni/src/main/native/libcore_net_RawSocket.cpp
index 4e5059f..2e493ac 100644
--- a/luni/src/main/native/libcore_net_RawSocket.cpp
+++ b/luni/src/main/native/libcore_net_RawSocket.cpp
@@ -38,10 +38,10 @@
#include <netinet/ip.h>
#include <linux/udp.h>
-union GCC_HIDDEN sockunion {
+union sockunion {
sockaddr sa;
sockaddr_ll sll;
-} su;
+};
/*
* Creates a socket suitable for raw socket operations. The socket is
@@ -61,6 +61,7 @@
return;
}
+ sockunion su;
memset(&su, 0, sizeof(su));
su.sll.sll_family = PF_PACKET;
su.sll.sll_protocol = htons(protocolType);
@@ -119,6 +120,7 @@
return 0;
}
+ sockunion su;
memset(&su, 0, sizeof(su));
su.sll.sll_hatype = htons(1); // ARPHRD_ETHER
su.sll.sll_halen = mac.size();
diff --git a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index d08b7d2..ce41a51 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -30,7 +30,7 @@
#include "unicode/unistr.h"
#include <string.h>
-#include <expat.h>
+#include <libexpat/expat.h>
#define BUCKET_COUNT 128
diff --git a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
index b9cfd19..a04bebf 100644
--- a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+++ b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -21,9 +21,11 @@
#define LOG_TAG "NativeCrypto"
#include <algorithm>
+#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <unistd.h>
+#include <vector>
#include <jni.h>
@@ -154,6 +156,35 @@
};
typedef UniquePtr<X509, X509_Delete> Unique_X509;
+class X509Chain {
+ public:
+ X509Chain(size_t n) : x509s_(n) {}
+
+ ~X509Chain() {
+ // TODO: C++0x auto
+ for (std::vector<X509*>::const_iterator it = x509s_.begin(); it != x509s_.end(); ++it) {
+ X509_free(*it);
+ }
+ }
+
+ X509*& operator[](size_t n) {
+ return x509s_[n];
+ }
+
+ X509* operator[](size_t n) const {
+ return x509s_[n];
+ }
+
+ X509* release(size_t i) {
+ X509* x = x509s_[i];
+ x509s_[i] = NULL;
+ return x;
+ }
+
+ private:
+ std::vector<X509*> x509s_;
+};
+
struct X509_NAME_Delete {
void operator()(X509_NAME* p) const {
X509_NAME_free(p);
@@ -203,6 +234,22 @@
ERR_remove_state(0);
}
+/**
+ * Throws a BadPaddingException with the given string as a message.
+ */
+static void throwBadPaddingException(JNIEnv* env, const char* message) {
+ JNI_TRACE("throwBadPaddingException %s", message);
+ jniThrowException(env, "javax/crypto/BadPaddingException", message);
+}
+
+/**
+ * Throws a SignatureException with the given string as a message.
+ */
+static void throwSignatureException(JNIEnv* env, const char* message) {
+ JNI_TRACE("throwSignatureException %s", message);
+ jniThrowException(env, "java/security/SignatureException", message);
+}
+
/*
* Checks this thread's OpenSSL error queue and throws a RuntimeException if
* necessary.
@@ -210,14 +257,30 @@
* @return true if an exception was thrown, false if not.
*/
static bool throwExceptionIfNecessary(JNIEnv* env, const char* location __attribute__ ((unused))) {
- int error = ERR_get_error();
+ const char* file;
+ int line;
+ const char* data;
+ int flags;
+ unsigned long error = ERR_get_error_line_data(&file, &line, &data, &flags);
int result = false;
if (error != 0) {
char message[256];
ERR_error_string_n(error, message, sizeof(message));
- JNI_TRACE("OpenSSL error in %s %d: %s", location, error, message);
- jniThrowRuntimeException(env, message);
+ int library = ERR_GET_LIB(error);
+ int reason = ERR_GET_REASON(error);
+ JNI_TRACE("OpenSSL error in %s error=%lx library=%x reason=%x (%s:%d): %s %s",
+ location, error, library, reason, file, line, message,
+ (flags & ERR_TXT_STRING) ? data : "(no data)");
+ if ((library == ERR_LIB_RSA)
+ && ((reason == RSA_R_BLOCK_TYPE_IS_NOT_01)
+ || (reason == RSA_R_BLOCK_TYPE_IS_NOT_02))) {
+ throwBadPaddingException(env, message);
+ } else if (library == ERR_LIB_RSA && reason == RSA_R_DATA_GREATER_THAN_MOD_LEN) {
+ throwSignatureException(env, message);
+ } else {
+ jniThrowRuntimeException(env, message);
+ }
result = true;
}
@@ -577,6 +640,27 @@
return static_cast<jint>(reinterpret_cast<uintptr_t>(e));
}
+static jint NativeCrypto_ENGINE_add(JNIEnv* env, jclass, jint engineRef) {
+ ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef));
+ JNI_TRACE("ENGINE_add(%p)", e);
+
+ if (e == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "engineRef == 0");
+ return 0;
+ }
+
+ int ret = ENGINE_add(e);
+
+ /*
+ * We tolerate errors, because the most likely error is that
+ * the ENGINE is already in the list.
+ */
+ freeOpenSslErrorState();
+
+ JNI_TRACE("ENGINE_add(%p) => %d", e, ret);
+ return ret;
+}
+
static jint NativeCrypto_ENGINE_init(JNIEnv* env, jclass, jint engineRef) {
ENGINE* e = reinterpret_cast<ENGINE*>(static_cast<uintptr_t>(engineRef));
JNI_TRACE("ENGINE_init(%p)", e);
@@ -1010,6 +1094,78 @@
return static_cast<jint>(reinterpret_cast<uintptr_t>(pkey.release()));
}
+static jint NativeCrypto_RSA_size(JNIEnv* env, jclass, jint pkeyRef) {
+ EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+ JNI_TRACE("RSA_size(%p)", pkey);
+
+ Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
+ if (rsa.get() == NULL) {
+ jniThrowRuntimeException(env, "RSA_size failed");
+ return 0;
+ }
+
+ return static_cast<jint>(RSA_size(rsa.get()));
+}
+
+typedef int RSACryptOperation(int flen, const unsigned char* from, unsigned char* to, RSA* rsa,
+ int padding);
+
+static jint RSA_crypt_operation(RSACryptOperation operation,
+ const char* caller __attribute__ ((unused)), JNIEnv* env, jint flen,
+ jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jint pkeyRef, jint padding) {
+ EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+ JNI_TRACE("%s(%d, %p, %p, %p)", caller, flen, fromJavaBytes, toJavaBytes, pkey);
+
+ Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
+ if (rsa.get() == NULL) {
+ return -1;
+ }
+
+ ScopedByteArrayRO from(env, fromJavaBytes);
+ if (from.get() == NULL) {
+ return -1;
+ }
+
+ ScopedByteArrayRW to(env, toJavaBytes);
+ if (to.get() == NULL) {
+ return -1;
+ }
+
+ int resultSize = operation(static_cast<int>(flen),
+ reinterpret_cast<const unsigned char*>(from.get()),
+ reinterpret_cast<unsigned char*>(to.get()), rsa.get(), padding);
+ if (resultSize == -1) {
+ JNI_TRACE("%s => failed", caller);
+ throwExceptionIfNecessary(env, "RSA_crypt_operation");
+ return -1;
+ }
+
+ JNI_TRACE("%s(%d, %p, %p, %p) => %d", caller, flen, fromJavaBytes, toJavaBytes, pkey,
+ resultSize);
+ return static_cast<jint>(resultSize);
+}
+
+static jint NativeCrypto_RSA_private_encrypt(JNIEnv* env, jclass, jint flen,
+ jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jint pkeyRef, jint padding) {
+ return RSA_crypt_operation(RSA_private_encrypt, __FUNCTION__,
+ env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding);
+}
+static jint NativeCrypto_RSA_public_decrypt(JNIEnv* env, jclass, jint flen,
+ jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jint pkeyRef, jint padding) {
+ return RSA_crypt_operation(RSA_public_decrypt, __FUNCTION__,
+ env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding);
+}
+static jint NativeCrypto_RSA_public_encrypt(JNIEnv* env, jclass, jint flen,
+ jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jint pkeyRef, jint padding) {
+ return RSA_crypt_operation(RSA_public_encrypt, __FUNCTION__,
+ env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding);
+}
+static jint NativeCrypto_RSA_private_decrypt(JNIEnv* env, jclass, jint flen,
+ jbyteArray fromJavaBytes, jbyteArray toJavaBytes, jint pkeyRef, jint padding) {
+ return RSA_crypt_operation(RSA_private_decrypt, __FUNCTION__,
+ env, flen, fromJavaBytes, toJavaBytes, pkeyRef, padding);
+}
+
/*
* public static native byte[][] get_RSA_public_params(int);
*/
@@ -1828,6 +1984,24 @@
return result;
}
+static void NativeCrypto_RAND_bytes(JNIEnv* env, jclass, jbyteArray output) {
+ JNI_TRACE("NativeCrypto_RAND_bytes(%p)", output);
+
+ ScopedByteArrayRW outputBytes(env, output);
+ if (outputBytes.get() == NULL) {
+ return;
+ }
+
+ unsigned char* tmp = reinterpret_cast<unsigned char*>(outputBytes.get());
+ if (!RAND_bytes(tmp, outputBytes.size())) {
+ throwExceptionIfNecessary(env, "NativeCrypto_RAND_bytes");
+ JNI_TRACE("tmp=%p NativeCrypto_RAND_bytes => threw error", tmp);
+ return;
+ }
+
+ JNI_TRACE("NativeCrypto_RAND_bytes(%p) => success", output);
+}
+
#ifdef WITH_JNI_TRACE
/**
* Based on example logging call back from SSL_CTX_set_info_callback man page
@@ -2364,11 +2538,19 @@
}
/**
- * Call back to ask for a client certificate
+ * Call back to ask for a client certificate. There are three possible exit codes:
+ *
+ * 1 is success. x509Out and pkeyOut should point to the correct private key and certificate.
+ * 0 is unable to find key. x509Out and pkeyOut should be NULL.
+ * -1 is error and it doesn't matter what x509Out and pkeyOut are.
*/
static int client_cert_cb(SSL* ssl, X509** x509Out, EVP_PKEY** pkeyOut) {
JNI_TRACE("ssl=%p client_cert_cb x509Out=%p pkeyOut=%p", ssl, x509Out, pkeyOut);
+ /* Clear output of key and certificate in case of early exit due to error. */
+ *x509Out = NULL;
+ *pkeyOut = NULL;
+
AppData* appData = toAppData(ssl);
JNIEnv* env = appData->env;
if (env == NULL) {
@@ -2378,7 +2560,7 @@
}
if (env->ExceptionCheck()) {
JNI_TRACE("ssl=%p client_cert_cb already pending exception => 0", ssl);
- return 0;
+ return -1;
}
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
@@ -2425,21 +2607,17 @@
if (env->ExceptionCheck()) {
JNI_TRACE("ssl=%p client_cert_cb exception => 0", ssl);
- return 0;
+ return -1;
}
// Check for values set from Java
X509* certificate = SSL_get_certificate(ssl);
EVP_PKEY* privatekey = SSL_get_privatekey(ssl);
- int result;
+ int result = 0;
if (certificate != NULL && privatekey != NULL) {
*x509Out = certificate;
*pkeyOut = privatekey;
result = 1;
- } else {
- *x509Out = NULL;
- *pkeyOut = NULL;
- result = 0;
}
JNI_TRACE("ssl=%p client_cert_cb => *x509=%p *pkey=%p %d", ssl, *x509Out, *pkeyOut, result);
return result;
@@ -2779,7 +2957,7 @@
return;
}
- Unique_X509 certificatesX509[length];
+ X509Chain certificatesX509(length);
for (int i = 0; i < length; i++) {
ScopedLocalRef<jbyteArray> certificate(env,
reinterpret_cast<jbyteArray>(env->GetObjectArrayElement(certificates, i)));
@@ -2795,9 +2973,9 @@
return;
}
const unsigned char* tmp = reinterpret_cast<const unsigned char*>(buf.get());
- certificatesX509[i].reset(d2i_X509(NULL, &tmp, buf.size()));
+ certificatesX509[i] = d2i_X509(NULL, &tmp, buf.size());
- if (certificatesX509[i].get() == NULL) {
+ if (certificatesX509[i] == NULL) {
ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error parsing certificate");
SSL_clear(ssl);
@@ -2806,9 +2984,9 @@
}
}
- int ret = SSL_use_certificate(ssl, certificatesX509[0].get());
+ int ret = SSL_use_certificate(ssl, certificatesX509[0]);
if (ret == 1) {
- OWNERSHIP_TRANSFERRED(certificatesX509[0]);
+ certificatesX509.release(0);
} else {
ALOGE("%s", ERR_error_string(ERR_peek_error(), NULL));
throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE, "Error setting certificate");
@@ -2824,7 +3002,7 @@
return;
}
for (int i = 1; i < length; i++) {
- if (!sk_X509_push(chain.get(), certificatesX509[i].release())) {
+ if (!sk_X509_push(chain.get(), certificatesX509.release(i))) {
jniThrowOutOfMemoryError(env, "Unable to push certificate");
JNI_TRACE("ssl=%p NativeCrypto_SSL_use_certificate => certificate push error", ssl);
return;
@@ -3186,8 +3364,10 @@
SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
AppData* appData = toAppData(ssl);
+ JNI_TRACE("AppData=%p", appData);
unsigned char* npnProtocols = reinterpret_cast<unsigned char*>(appData->npnProtocolsData);
size_t npnProtocolsLength = appData->npnProtocolsLength;
+ JNI_TRACE("npn_protocols=%p, length=%d", npnProtocols, npnProtocolsLength);
int status = SSL_select_next_proto(out, outlen, in, inlen, npnProtocols, npnProtocolsLength);
switch (status) {
@@ -3937,7 +4117,7 @@
}
/**
- * Interrupt any pending IO before closing the socket.
+ * Interrupt any pending I/O before closing the socket.
*/
static void NativeCrypto_SSL_interrupt(
JNIEnv* env, jclass, jint ssl_address) {
@@ -4187,6 +4367,23 @@
const unsigned char* ucp = reinterpret_cast<const unsigned char*>(bytes.get());
SSL_SESSION* ssl_session = d2i_SSL_SESSION(NULL, &ucp, bytes.size());
+ // Initialize SSL_SESSION cipher field based on cipher_id http://b/7091840
+ if (ssl_session != NULL) {
+ // based on ssl_get_prev_session
+ uint32_t cipher_id_network_order = htonl(ssl_session->cipher_id);
+ uint8_t* cipher_id_byte_pointer = reinterpret_cast<uint8_t*>(&cipher_id_network_order);
+ if (ssl_session->ssl_version >= SSL3_VERSION_MAJOR) {
+ cipher_id_byte_pointer += 2; // skip first two bytes for SSL3+
+ } else {
+ cipher_id_byte_pointer += 1; // skip first byte for SSL2
+ }
+ ssl_session->cipher = SSLv23_method()->get_cipher_by_char(cipher_id_byte_pointer);
+ JNI_TRACE("NativeCrypto_d2i_SSL_SESSION cipher_id=%lx hton=%x 0=%x 1=%x cipher=%s",
+ ssl_session->cipher_id, cipher_id_network_order,
+ cipher_id_byte_pointer[0], cipher_id_byte_pointer[1],
+ SSL_CIPHER_get_name(ssl_session->cipher));
+ }
+
JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => %p", ssl_session);
return static_cast<jint>(reinterpret_cast<uintptr_t>(ssl_session));
}
@@ -4197,6 +4394,7 @@
NATIVE_METHOD(NativeCrypto, clinit, "()V"),
NATIVE_METHOD(NativeCrypto, ENGINE_load_dynamic, "()V"),
NATIVE_METHOD(NativeCrypto, ENGINE_by_id, "(Ljava/lang/String;)I"),
+ NATIVE_METHOD(NativeCrypto, ENGINE_add, "(I)I"),
NATIVE_METHOD(NativeCrypto, ENGINE_init, "(I)I"),
NATIVE_METHOD(NativeCrypto, ENGINE_finish, "(I)I"),
NATIVE_METHOD(NativeCrypto, ENGINE_free, "(I)I"),
@@ -4211,6 +4409,11 @@
NATIVE_METHOD(NativeCrypto, i2d_PUBKEY, "(I)[B"),
NATIVE_METHOD(NativeCrypto, d2i_PUBKEY, "([B)I"),
NATIVE_METHOD(NativeCrypto, RSA_generate_key_ex, "(I[B)I"),
+ NATIVE_METHOD(NativeCrypto, RSA_size, "(I)I"),
+ NATIVE_METHOD(NativeCrypto, RSA_private_encrypt, "(I[B[BII)I"),
+ NATIVE_METHOD(NativeCrypto, RSA_public_decrypt, "(I[B[BII)I"),
+ NATIVE_METHOD(NativeCrypto, RSA_public_encrypt, "(I[B[BII)I"),
+ NATIVE_METHOD(NativeCrypto, RSA_private_decrypt, "(I[B[BII)I"),
NATIVE_METHOD(NativeCrypto, get_RSA_private_params, "(I)[[B"),
NATIVE_METHOD(NativeCrypto, get_RSA_public_params, "(I)[[B"),
NATIVE_METHOD(NativeCrypto, DSA_generate_key, "(I[B[B[B[B)I"),
@@ -4236,6 +4439,7 @@
NATIVE_METHOD(NativeCrypto, EVP_CIPHER_CTX_cleanup, "(I)V"),
NATIVE_METHOD(NativeCrypto, RAND_seed, "([B)V"),
NATIVE_METHOD(NativeCrypto, RAND_load_file, "(Ljava/lang/String;J)I"),
+ NATIVE_METHOD(NativeCrypto, RAND_bytes, "([B)V"),
NATIVE_METHOD(NativeCrypto, SSL_CTX_new, "()I"),
NATIVE_METHOD(NativeCrypto, SSL_CTX_free, "(I)V"),
NATIVE_METHOD(NativeCrypto, SSL_CTX_set_session_id_context, "(I[B)V"),
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index b67348b..d5705a6 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -52,7 +52,6 @@
valueOf.cpp
LOCAL_C_INCLUDES += \
- external/expat/lib \
external/icu4c/common \
external/icu4c/i18n \
external/openssl/include \
diff --git a/luni/src/test/java/libcore/icu/ICUTest.java b/luni/src/test/java/libcore/icu/ICUTest.java
index 9282167..a7e732c 100644
--- a/luni/src/test/java/libcore/icu/ICUTest.java
+++ b/luni/src/test/java/libcore/icu/ICUTest.java
@@ -53,4 +53,12 @@
assertEquals(new Locale("", "", "POSIX"), ICU.localeFromString("__POSIX"));
assertEquals(new Locale("aa", "BB", "CC"), ICU.localeFromString("aa_BB_CC"));
}
+
+ public void test_getScript_addLikelySubtags() throws Exception {
+ assertEquals("Latn", ICU.getScript(ICU.addLikelySubtags("en_US")));
+ assertEquals("Hebr", ICU.getScript(ICU.addLikelySubtags("he")));
+ assertEquals("Hebr", ICU.getScript(ICU.addLikelySubtags("he_IL")));
+ assertEquals("Hebr", ICU.getScript(ICU.addLikelySubtags("iw")));
+ assertEquals("Hebr", ICU.getScript(ICU.addLikelySubtags("iw_IL")));
+ }
}
diff --git a/luni/src/test/java/libcore/icu/LocaleDataTest.java b/luni/src/test/java/libcore/icu/LocaleDataTest.java
new file mode 100644
index 0000000..e412a1d
--- /dev/null
+++ b/luni/src/test/java/libcore/icu/LocaleDataTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 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.icu;
+
+import java.util.Locale;
+
+public class LocaleDataTest extends junit.framework.TestCase {
+ public void testAll() throws Exception {
+ // Test that we can get the locale data for all known locales.
+ for (Locale l : Locale.getAvailableLocales()) {
+ LocaleData d = LocaleData.get(l);
+ System.err.println(l + " : " + d.yesterday + " " + d.today + " " + d.tomorrow);
+ }
+ }
+
+ public void test_en_US() throws Exception {
+ LocaleData l = LocaleData.get(Locale.US);
+ assertEquals("AM", l.amPm[0]);
+ assertEquals("BC", l.eras[0]);
+
+ assertEquals("January", l.longMonthNames[0]);
+ assertEquals("Jan", l.shortMonthNames[0]);
+ assertEquals("J", l.tinyMonthNames[0]);
+
+ assertEquals("January", l.longStandAloneMonthNames[0]);
+ assertEquals("Jan", l.shortStandAloneMonthNames[0]);
+ assertEquals("J", l.tinyStandAloneMonthNames[0]);
+
+ assertEquals("Sunday", l.longWeekdayNames[1]);
+ assertEquals("Sun", l.shortWeekdayNames[1]);
+ assertEquals("S", l.tinyWeekdayNames[1]);
+
+ assertEquals("Sunday", l.longStandAloneWeekdayNames[1]);
+ assertEquals("Sun", l.shortStandAloneWeekdayNames[1]);
+ assertEquals("S", l.tinyStandAloneWeekdayNames[1]);
+
+ assertEquals("Yesterday", l.yesterday);
+ assertEquals("Today", l.today);
+ assertEquals("Tomorrow", l.tomorrow);
+ }
+
+ public void test_de_DE() throws Exception {
+ LocaleData l = LocaleData.get(new Locale("de", "DE"));
+
+ assertEquals("Gestern", l.yesterday);
+ assertEquals("Heute", l.today);
+ assertEquals("Morgen", l.tomorrow);
+ }
+
+ public void test_cs_CZ() throws Exception {
+ LocaleData l = LocaleData.get(new Locale("cs", "CZ"));
+
+ assertEquals("ledna", l.longMonthNames[0]);
+ assertEquals("Led", l.shortMonthNames[0]);
+ assertEquals("1", l.tinyMonthNames[0]);
+
+ assertEquals("leden", l.longStandAloneMonthNames[0]);
+ assertEquals("1.", l.shortStandAloneMonthNames[0]);
+ assertEquals("l", l.tinyStandAloneMonthNames[0]);
+ }
+
+ public void test_ru_RU() throws Exception {
+ LocaleData l = LocaleData.get(new Locale("ru", "RU"));
+
+ assertEquals("воскресенье", l.longWeekdayNames[1]);
+ assertEquals("вс", l.shortWeekdayNames[1]);
+ assertEquals("В", l.tinyWeekdayNames[1]);
+
+ // Russian stand-alone weekday names get an initial capital.
+ assertEquals("Воскресенье", l.longStandAloneWeekdayNames[1]);
+ assertEquals("Вс", l.shortStandAloneWeekdayNames[1]);
+ assertEquals("В", l.tinyStandAloneWeekdayNames[1]);
+ }
+}
diff --git a/luni/src/test/java/libcore/io/DiskLruCacheTest.java b/luni/src/test/java/libcore/io/DiskLruCacheTest.java
index 03a6932..2796b65 100644
--- a/luni/src/test/java/libcore/io/DiskLruCacheTest.java
+++ b/luni/src/test/java/libcore/io/DiskLruCacheTest.java
@@ -349,6 +349,28 @@
creator2.commit();
}
+ public void testCreateNewEntryWithMissingFileAborts() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ creator.set(0, "A");
+ creator.set(1, "A");
+ assertTrue(getDirtyFile("k1", 0).exists());
+ assertTrue(getDirtyFile("k1", 1).exists());
+ assertTrue(getDirtyFile("k1", 0).delete());
+ assertFalse(getDirtyFile("k1", 0).exists());
+ creator.commit(); // silently abort if file does not exist due to I/O issue
+
+ assertFalse(getCleanFile("k1", 0).exists());
+ assertFalse(getCleanFile("k1", 1).exists());
+ assertFalse(getDirtyFile("k1", 0).exists());
+ assertFalse(getDirtyFile("k1", 1).exists());
+ assertNull(cache.get("k1"));
+
+ DiskLruCache.Editor creator2 = cache.edit("k1");
+ creator2.set(0, "B");
+ creator2.set(1, "C");
+ creator2.commit();
+ }
+
public void testRevertWithTooFewValues() throws Exception {
DiskLruCache.Editor creator = cache.edit("k1");
creator.set(1, "A");
@@ -805,4 +827,4 @@
assertTrue(getCleanFile(key, 1).exists());
snapshot.close();
}
-}
\ No newline at end of file
+}
diff --git a/luni/src/test/java/libcore/io/MemoryTest.java b/luni/src/test/java/libcore/io/MemoryTest.java
index a533f15..9a596fb 100644
--- a/luni/src/test/java/libcore/io/MemoryTest.java
+++ b/luni/src/test/java/libcore/io/MemoryTest.java
@@ -18,6 +18,7 @@
package libcore.io;
import dalvik.system.VMRuntime;
+import java.util.Arrays;
import junit.framework.TestCase;
public class MemoryTest extends TestCase {
@@ -28,65 +29,116 @@
swappedValues[i] = Integer.reverseBytes(values[i]);
}
- int scale = 4;
+ int scale = SizeOf.INT;
VMRuntime runtime = VMRuntime.getRuntime();
- byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, scale * values.length);
- int ptr = (int) runtime.addressOf(array);
+ byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, scale * values.length + 1);
+ int base_ptr = (int) runtime.addressOf(array);
- // Regular copy.
- Memory.pokeIntArray(ptr, values, 0, values.length, false);
- assertIntsEqual(values, ptr, false);
- assertIntsEqual(swappedValues, ptr, true);
+ for (int ptr_offset = 0; ptr_offset < 2; ++ptr_offset) {
+ int ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
+ Arrays.fill(array, (byte) 0);
- // Swapped copy.
- Memory.pokeIntArray(ptr, values, 0, values.length, true);
- assertIntsEqual(values, ptr, true);
- assertIntsEqual(swappedValues, ptr, false);
+ // Regular copy.
+ Memory.pokeIntArray(ptr, values, 0, values.length, false);
+ assertIntsEqual(values, ptr, false);
+ assertIntsEqual(swappedValues, ptr, true);
- // Swapped copies of slices (to ensure we test non-zero offsets).
- for (int i = 0; i < values.length; ++i) {
- Memory.pokeIntArray(ptr + i * scale, values, i, 1, true);
+ // Swapped copy.
+ Memory.pokeIntArray(ptr, values, 0, values.length, true);
+ assertIntsEqual(values, ptr, true);
+ assertIntsEqual(swappedValues, ptr, false);
+
+ // Swapped copies of slices (to ensure we test non-zero offsets).
+ for (int i = 0; i < values.length; ++i) {
+ Memory.pokeIntArray(ptr + i * scale, values, i, 1, true);
+ }
+ assertIntsEqual(values, ptr, true);
+ assertIntsEqual(swappedValues, ptr, false);
}
- assertIntsEqual(values, ptr, true);
- assertIntsEqual(swappedValues, ptr, false);
}
private void assertIntsEqual(int[] expectedValues, int ptr, boolean swap) {
for (int i = 0; i < expectedValues.length; ++i) {
- assertEquals(expectedValues[i], Memory.peekInt(ptr + 4 * i, swap));
+ assertEquals(expectedValues[i], Memory.peekInt(ptr + SizeOf.INT * i, swap));
}
}
+ public void testSetLongArray() {
+ long[] values = { 0x1020304050607080L, 0xffeeddccbbaa9988L };
+ long[] swappedValues = new long[values.length];
+ for (int i = 0; i < values.length; ++i) {
+ swappedValues[i] = Long.reverseBytes(values[i]);
+ }
+
+ int scale = SizeOf.LONG;
+ VMRuntime runtime = VMRuntime.getRuntime();
+ byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, scale * values.length + 1);
+ int base_ptr = (int) runtime.addressOf(array);
+
+ for (int ptr_offset = 0; ptr_offset < 2; ++ptr_offset) {
+ int ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
+ Arrays.fill(array, (byte) 0);
+
+ // Regular copy.
+ Memory.pokeLongArray(ptr, values, 0, values.length, false);
+ assertLongsEqual(values, ptr, false);
+ assertLongsEqual(swappedValues, ptr, true);
+
+ // Swapped copy.
+ Memory.pokeLongArray(ptr, values, 0, values.length, true);
+ assertLongsEqual(values, ptr, true);
+ assertLongsEqual(swappedValues, ptr, false);
+
+ // Swapped copies of slices (to ensure we test non-zero offsets).
+ for (int i = 0; i < values.length; ++i) {
+ Memory.pokeLongArray(ptr + i * scale, values, i, 1, true);
+ }
+ assertLongsEqual(values, ptr, true);
+ assertLongsEqual(swappedValues, ptr, false);
+ }
+ }
+
+ private void assertLongsEqual(long[] expectedValues, int ptr, boolean swap) {
+ for (int i = 0; i < expectedValues.length; ++i) {
+ assertEquals(expectedValues[i], Memory.peekLong(ptr + SizeOf.LONG * i, swap));
+ }
+ }
+
public void testSetShortArray() {
short[] values = { 0x0001, 0x0020, 0x0300, 0x4000 };
short[] swappedValues = { 0x0100, 0x2000, 0x0003, 0x0040 };
- int scale = 2;
+ int scale = SizeOf.SHORT;
VMRuntime runtime = VMRuntime.getRuntime();
- byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, scale * values.length);
- int ptr = (int) runtime.addressOf(array);
+ byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, scale * values.length + 1);
+ int base_ptr = (int) runtime.addressOf(array);
- // Regular copy. Memset first so we start from a known state.
- Memory.pokeShortArray(ptr, values, 0, values.length, false);
- assertShortsEqual(values, ptr, false);
- assertShortsEqual(swappedValues, ptr, true);
+ for (int ptr_offset = 0; ptr_offset < 2; ++ptr_offset) {
+ int ptr = base_ptr + ptr_offset; // To test aligned and unaligned accesses.
+ Arrays.fill(array, (byte) 0);
- // Swapped copy.
- Memory.pokeShortArray(ptr, values, 0, values.length, true);
- assertShortsEqual(values, ptr, true);
- assertShortsEqual(swappedValues, ptr, false);
+ // Regular copy.
+ Memory.pokeShortArray(ptr, values, 0, values.length, false);
+ assertShortsEqual(values, ptr, false);
+ assertShortsEqual(swappedValues, ptr, true);
- // Swapped copies of slices (to ensure we test non-zero offsets).
- for (int i = 0; i < values.length; ++i) {
- Memory.pokeShortArray(ptr + i * scale, values, i, 1, true);
+ // Swapped copy.
+ Memory.pokeShortArray(ptr, values, 0, values.length, true);
+ assertShortsEqual(values, ptr, true);
+ assertShortsEqual(swappedValues, ptr, false);
+
+ // Swapped copies of slices (to ensure we test non-zero offsets).
+ for (int i = 0; i < values.length; ++i) {
+ Memory.pokeShortArray(ptr + i * scale, values, i, 1, true);
+ }
+ assertShortsEqual(values, ptr, true);
+ assertShortsEqual(swappedValues, ptr, false);
}
- assertShortsEqual(values, ptr, true);
- assertShortsEqual(swappedValues, ptr, false);
}
private void assertShortsEqual(short[] expectedValues, int ptr, boolean swap) {
for (int i = 0; i < expectedValues.length; ++i) {
- assertEquals(expectedValues[i], Memory.peekShort(ptr + 2 * i, swap));
+ assertEquals(expectedValues[i], Memory.peekShort(ptr + SizeOf.SHORT * i, swap));
}
}
}
diff --git a/luni/src/test/java/libcore/io/StrictLineReaderTest.java b/luni/src/test/java/libcore/io/StrictLineReaderTest.java
new file mode 100644
index 0000000..d5d3381
--- /dev/null
+++ b/luni/src/test/java/libcore/io/StrictLineReaderTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStream;
+import java.nio.charset.Charsets;
+
+public class StrictLineReaderTest extends TestCase {
+
+ public void testLineReaderConsistencyWithReadAsciiLine () {
+ try {
+ // Testing with LineReader buffer capacity 32 to check some corner cases.
+ StrictLineReader lineReader = new StrictLineReader(createTestInputStream(), 32,
+ Charsets.US_ASCII);
+ InputStream refStream = createTestInputStream();
+ while (true) {
+ try {
+ String refLine = Streams.readAsciiLine(refStream);
+ try {
+ String line = lineReader.readLine();
+ if (!refLine.equals(line)) {
+ fail("line (\""+line+"\") differs from expected (\""+refLine+"\").");
+ }
+ } catch (EOFException eof) {
+ fail("line reader threw EOFException too early.");
+ }
+ } catch (EOFException refEof) {
+ try {
+ lineReader.readLine();
+ fail("line reader didn't throw the expected EOFException.");
+ } catch (EOFException eof) {
+ // OK
+ break;
+ }
+ }
+ }
+ refStream.close();
+ lineReader.close();
+ } catch (IOException ioe) {
+ fail("Unexpected IOException " + ioe.toString());
+ }
+ }
+
+ private InputStream createTestInputStream() {
+ return new ByteArrayInputStream((
+ /* each source lines below should represent 32 bytes, until the next comment */
+ "12 byte line\n18 byte line......\n" +
+ "pad\nline spanning two 32-byte bu" +
+ "ffers\npad......................\n" +
+ "pad\nline spanning three 32-byte " +
+ "buffers and ending with LF at th" +
+ "e end of a 32 byte buffer......\n" +
+ "pad\nLine ending with CRLF split" +
+ " at the end of a 32-byte buffer\r" +
+ "\npad...........................\n" +
+ /* end of 32-byte lines */
+ "line ending with CRLF\r\n" +
+ "this is a long line with embedded CR \r ending with CRLF and having more than " +
+ "32 characters\r\n" +
+ "unterminated line - should be dropped"
+ ).getBytes());
+ }
+}
+
diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java
index 3cf621e..b2391ac 100644
--- a/luni/src/test/java/libcore/java/io/FileTest.java
+++ b/luni/src/test/java/libcore/java/io/FileTest.java
@@ -225,4 +225,45 @@
assertTrue(new File("/").getTotalSpace() >= 0);
assertTrue(new File("/").getUsableSpace() >= 0);
}
+
+ public void test_mkdirs() throws Exception {
+ // Set up a directory to test in.
+ File base = createTemporaryDirectory();
+
+ // mkdirs returns true only if it _creates_ a directory.
+ // So we get false for a directory that already exists...
+ assertTrue(base.exists());
+ assertFalse(base.mkdirs());
+ // But true if we had to create something.
+ File a = new File(base, "a");
+ assertFalse(a.exists());
+ assertTrue(a.mkdirs());
+ assertTrue(a.exists());
+
+ // Test the recursive case where we need to create multiple parents.
+ File b = new File(a, "b");
+ File c = new File(b, "c");
+ File d = new File(c, "d");
+ assertTrue(a.exists());
+ assertFalse(b.exists());
+ assertFalse(c.exists());
+ assertFalse(d.exists());
+ assertTrue(d.mkdirs());
+ assertTrue(a.exists());
+ assertTrue(b.exists());
+ assertTrue(c.exists());
+ assertTrue(d.exists());
+
+ // Test the case where the 'directory' exists as a file.
+ File existsAsFile = new File(base, "existsAsFile");
+ existsAsFile.createNewFile();
+ assertTrue(existsAsFile.exists());
+ assertFalse(existsAsFile.mkdirs());
+
+ // Test the case where the parent exists as a file.
+ File badParent = new File(existsAsFile, "sub");
+ assertTrue(existsAsFile.exists());
+ assertFalse(badParent.exists());
+ assertFalse(badParent.mkdirs());
+ }
}
diff --git a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
old mode 100644
new mode 100755
index e973b8f..e1f51bd
--- a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
@@ -28,6 +28,7 @@
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.ClosedChannelException;
import java.nio.channels.Pipe;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.ServerSocketChannel;
@@ -93,7 +94,7 @@
public void testInterruptWritableSocketChannel() throws Exception {
sockets = newSocketChannelPair();
- testInterruptReadableChannel(sockets[0].getChannel());
+ testInterruptWritableChannel(sockets[0].getChannel());
}
/**
@@ -166,6 +167,7 @@
channel.write(ByteBuffer.allocate(BUFFER_SIZE));
}
} catch (ClosedByInterruptException expected) {
+ } catch (ClosedChannelException expected) {
}
}
@@ -173,6 +175,10 @@
final Thread toInterrupt = Thread.currentThread();
new Thread(new Runnable () {
@Override public void run() {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ex) {
+ }
toInterrupt.interrupt();
}
}).start();
diff --git a/luni/src/test/java/libcore/java/io/SerializationTest.java b/luni/src/test/java/libcore/java/io/SerializationTest.java
index 434dd56..d452c11 100644
--- a/luni/src/test/java/libcore/java/io/SerializationTest.java
+++ b/luni/src/test/java/libcore/java/io/SerializationTest.java
@@ -18,6 +18,8 @@
import java.io.IOException;
import java.io.InvalidClassException;
+import java.io.ObjectStreamClass;
+import java.io.ObjectStreamField;
import java.io.Serializable;
import junit.framework.TestCase;
import libcore.util.SerializationTester;
@@ -26,6 +28,13 @@
// http://b/4471249
public void testSerializeFieldMadeTransient() throws Exception {
+ // Does ObjectStreamClass have the right idea?
+ ObjectStreamClass osc = ObjectStreamClass.lookup(FieldMadeTransient.class);
+ ObjectStreamField[] fields = osc.getFields();
+ assertEquals(1, fields.length);
+ assertEquals("nonTransientInt", fields[0].getName());
+ assertEquals(int.class, fields[0].getType());
+
// this was created by serializing a FieldMadeTransient with a non-0 transientInt
String s = "aced0005737200346c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+ "374244669656c644d6164655472616e7369656e74000000000000000002000149000c7472616e736"
@@ -37,6 +46,7 @@
static class FieldMadeTransient implements Serializable {
private static final long serialVersionUID = 0L;
private transient int transientInt;
+ private int nonTransientInt;
}
public void testSerialVersionUidChange() throws Exception {
diff --git a/luni/src/test/java/libcore/java/lang/IntrinsicTest.java b/luni/src/test/java/libcore/java/lang/IntrinsicTest.java
index fcf0522..6425b85 100644
--- a/luni/src/test/java/libcore/java/lang/IntrinsicTest.java
+++ b/luni/src/test/java/libcore/java/lang/IntrinsicTest.java
@@ -18,6 +18,9 @@
import junit.framework.TestCase;
+/**
+ * Tests that all intrinsic methods are still invokable via reflection.
+ */
public final class IntrinsicTest extends TestCase {
public void testString_charAt() throws Exception {
"hello".charAt(0);
@@ -60,6 +63,32 @@
Math.class.getMethod("abs", double.class).invoke(null, 1.0);
}
+ public void testStrictMath_abs() throws Exception {
+ StrictMath.abs(1);
+ StrictMath.class.getMethod("abs", int.class).invoke(null, 1);
+ StrictMath.abs(1L);
+ StrictMath.class.getMethod("abs", long.class).invoke(null, 1L);
+ StrictMath.abs(1.0f);
+ StrictMath.class.getMethod("abs", float.class).invoke(null, 1.0f);
+ StrictMath.abs(1.0);
+ StrictMath.class.getMethod("abs", double.class).invoke(null, 1.0);
+ }
+
+ public void testStrictMath_min() throws Exception {
+ StrictMath.min(1, 2);
+ StrictMath.class.getMethod("min", int.class, int.class).invoke(null, 1, 2);
+ }
+
+ public void testStrictMath_max() throws Exception {
+ StrictMath.max(1, 2);
+ StrictMath.class.getMethod("max", int.class, int.class).invoke(null, 1, 2);
+ }
+
+ public void testStrictMath_sqrt() throws Exception {
+ StrictMath.sqrt(2.0);
+ StrictMath.class.getMethod("sqrt", double.class).invoke(null, 2.0);
+ }
+
public void testMath_min() throws Exception {
Math.min(1, 2);
Math.class.getMethod("min", int.class, int.class).invoke(null, 1, 2);
diff --git a/luni/src/test/java/libcore/java/lang/StringTest.java b/luni/src/test/java/libcore/java/lang/StringTest.java
index 42a7aad..99dba49 100644
--- a/luni/src/test/java/libcore/java/lang/StringTest.java
+++ b/luni/src/test/java/libcore/java/lang/StringTest.java
@@ -91,10 +91,24 @@
}
}
- public void testStringFromCharset() {
- Charset cs = Charset.forName("UTF-8");
- byte[] bytes = new byte[] {(byte) 'h', (byte) 'i'};
- assertEquals("hi", new String(bytes, cs));
+ public void testString_BII() throws Exception {
+ byte[] bytes = "xa\u0666bx".getBytes("UTF-8");
+ assertEquals("a\u0666b", new String(bytes, 1, bytes.length - 2));
+ }
+
+ public void testString_BIIString() throws Exception {
+ byte[] bytes = "xa\u0666bx".getBytes("UTF-8");
+ assertEquals("a\u0666b", new String(bytes, 1, bytes.length - 2, "UTF-8"));
+ }
+
+ public void testString_BIICharset() throws Exception {
+ byte[] bytes = "xa\u0666bx".getBytes("UTF-8");
+ assertEquals("a\u0666b", new String(bytes, 1, bytes.length - 2, Charset.forName("UTF-8")));
+ }
+
+ public void testString_BCharset() throws Exception {
+ byte[] bytes = "a\u0666b".getBytes("UTF-8");
+ assertEquals("a\u0666b", new String(bytes, Charset.forName("UTF-8")));
}
public void testStringFromCharset_MaliciousCharset() {
diff --git a/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java b/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java
index 10a26fe..ef303bd 100644
--- a/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java
+++ b/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java
@@ -29,18 +29,42 @@
FinalizationTester.induceFinalization();
if (!finalized.get()) {
- fail();
+ fail("object not yet finalized");
+ }
+ }
+
+ /**
+ * Test verifies that runFinalization() does not mess up objects
+ * that should be finalized later on. http://b/6907299
+ */
+ public void testInducedFinalization() throws Exception {
+ AtomicBoolean finalized1 = new AtomicBoolean();
+ AtomicBoolean finalized2 = new AtomicBoolean();
+ createFinalizableObject(finalized1);
+ createFinalizableObject(finalized2);
+ FinalizationTester.induceFinalization();
+ if (!finalized1.get() || !finalized2.get()) {
+ fail("not yet finalized: " + finalized1.get() + " " + finalized2.get());
}
}
/** Do not inline this method; that could break non-precise GCs. See FinalizationTester. */
- private void createFinalizableObject(final AtomicBoolean finalized) {
- new X() {
+ private X createFinalizableObject(final AtomicBoolean finalized) {
+ X result = new X() {
@Override protected void finalize() throws Throwable {
super.finalize();
finalized.set(true);
}
};
+ FinalizationTester.induceFinalization();
+ // Dance around a bit to discourage dx from realizing that 'result' is no longer live.
+ boolean wasFinalized = finalized.get();
+ if (wasFinalized) {
+ fail("finalizer called early"); // ...because 'result' is still live until we return.
+ }
+ // But we don't actually want to return 'result' because then we'd have to worry about
+ // the caller accidentally keeping it live.
+ return wasFinalized ? result : null;
}
static class X {}
diff --git a/luni/src/test/java/libcore/java/net/OldDatagramPacketTest.java b/luni/src/test/java/libcore/java/net/OldDatagramPacketTest.java
index a77a44d..8ca4067 100644
--- a/luni/src/test/java/libcore/java/net/OldDatagramPacketTest.java
+++ b/luni/src/test/java/libcore/java/net/OldDatagramPacketTest.java
@@ -21,71 +21,40 @@
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
-import tests.support.Support_PortManager;
public class OldDatagramPacketTest extends junit.framework.TestCase {
- DatagramPacket dp;
-
- volatile boolean started = false;
-
- public void test_getPort() throws IOException {
- dp = new DatagramPacket("Hello".getBytes(), 5, InetAddress.getLocalHost(), 1000);
+ public void test_getPort() throws Exception {
+ DatagramPacket dp = new DatagramPacket("Hello".getBytes(), 5, InetAddress.getLocalHost(), 1000);
assertEquals("Incorrect port returned", 1000, dp.getPort());
- InetAddress localhost = InetAddress.getByName("localhost");
-
- int[] ports = Support_PortManager.getNextPortsForUDP(2);
- final int port = ports[0];
- final Object lock = new Object();
-
+ final DatagramSocket ss = new DatagramSocket();
Thread thread = new Thread(new Runnable() {
public void run() {
- DatagramSocket socket = null;
try {
- socket = new DatagramSocket(port);
- synchronized (lock) {
- started = true;
- lock.notifyAll();
- }
- socket.setSoTimeout(3000);
- DatagramPacket packet = new DatagramPacket(new byte[256],
- 256);
- socket.receive(packet);
- socket.send(packet);
- socket.close();
+ DatagramPacket packet = new DatagramPacket(new byte[256], 256);
+ ss.setSoTimeout(3000);
+ ss.receive(packet);
+ ss.send(packet);
} catch (IOException e) {
System.out.println("thread exception: " + e);
- if (socket != null)
- socket.close();
}
}
});
thread.start();
- DatagramSocket socket = null;
+ DatagramSocket cs = new DatagramSocket();
try {
- socket = new DatagramSocket(ports[1]);
- socket.setSoTimeout(3000);
- DatagramPacket packet = new DatagramPacket(new byte[] { 1, 2, 3, 4,
- 5, 6 }, 6, localhost, port);
- synchronized (lock) {
- try {
- if (!started)
- lock.wait();
- } catch (InterruptedException e) {
- fail(e.toString());
- }
- }
- socket.send(packet);
- socket.receive(packet);
- socket.close();
- assertTrue("datagram received wrong port: " + packet.getPort(),
- packet.getPort() == port);
+ byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6 };
+ DatagramPacket packet = new DatagramPacket(bytes, 6, InetAddress.getByName("localhost"), ss.getLocalPort());
+ cs.send(packet);
+ cs.setSoTimeout(3000);
+ cs.receive(packet);
+ cs.close();
+ assertEquals(packet.getPort(), ss.getLocalPort());
} finally {
- if (socket != null) {
- socket.close();
- }
+ cs.close();
+ ss.close();
}
}
@@ -104,7 +73,7 @@
}
public void test_setData$BII() {
- dp = new DatagramPacket("Hello".getBytes(), 5);
+ DatagramPacket dp = new DatagramPacket("Hello".getBytes(), 5);
try {
dp.setData(null, 2, 3);
fail("NullPointerException was not thrown.");
@@ -113,7 +82,7 @@
}
public void test_setData$B() {
- dp = new DatagramPacket("Hello".getBytes(), 5);
+ DatagramPacket dp = new DatagramPacket("Hello".getBytes(), 5);
try {
dp.setData(null);
fail("NullPointerException was not thrown.");
diff --git a/luni/src/test/java/libcore/java/net/OldServerSocketTest.java b/luni/src/test/java/libcore/java/net/OldServerSocketTest.java
index cf35489..6518897 100644
--- a/luni/src/test/java/libcore/java/net/OldServerSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/OldServerSocketTest.java
@@ -33,7 +33,6 @@
import java.nio.channels.ServerSocketChannel;
import java.security.Permission;
import java.util.Properties;
-import tests.support.Support_PortManager;
public class OldServerSocketTest extends OldSocketTestCase {
@@ -86,10 +85,9 @@
}
public void test_ConstructorII() throws IOException {
- int freePortNumber = Support_PortManager.getNextPort();
- s = new ServerSocket(freePortNumber, 1);
+ s = new ServerSocket(0, 1);
s.setSoTimeout(2000);
- startClient(freePortNumber);
+ startClient(s.getLocalPort());
sconn = s.accept();
sconn.close();
s.close();
@@ -133,10 +131,9 @@
}
public void test_ConstructorI() throws Exception {
- int portNumber = Support_PortManager.getNextPort();
- s = new ServerSocket(portNumber);
+ s = new ServerSocket(0);
try {
- new ServerSocket(portNumber);
+ new ServerSocket(s.getLocalPort());
fail("IOException was not thrown.");
} catch(IOException ioe) {
//expected
@@ -162,11 +159,9 @@
}
public void test_ConstructorIILjava_net_InetAddress() throws IOException {
- int freePortNumber = Support_PortManager.getNextPort();
-
- ServerSocket ss = new ServerSocket(freePortNumber, 10, InetAddress.getLocalHost());
+ ServerSocket ss = new ServerSocket(0, 10, InetAddress.getLocalHost());
try {
- new ServerSocket(freePortNumber, 10, InetAddress.getLocalHost());
+ new ServerSocket(ss.getLocalPort(), 10, InetAddress.getLocalHost());
fail("IOException was not thrown.");
} catch(IOException expected) {
}
@@ -217,9 +212,7 @@
}
public void test_accept() throws IOException {
- int portNumber = Support_PortManager.getNextPort();
-
- ServerSocket newSocket = new ServerSocket(portNumber);
+ ServerSocket newSocket = new ServerSocket(0);
newSocket.setSoTimeout(500);
try {
Socket accepted = newSocket.accept();
diff --git a/luni/src/test/java/libcore/java/net/OldURLClassLoaderTest.java b/luni/src/test/java/libcore/java/net/OldURLClassLoaderTest.java
index 2646f98..3a5608c 100644
--- a/luni/src/test/java/libcore/java/net/OldURLClassLoaderTest.java
+++ b/luni/src/test/java/libcore/java/net/OldURLClassLoaderTest.java
@@ -35,7 +35,6 @@
import java.util.jar.Manifest;
import org.apache.harmony.security.tests.support.TestCertUtils;
import tests.support.Support_Configuration;
-import tests.support.Support_PortManager;
import tests.support.Support_TestWebData;
import tests.support.Support_TestWebServer;
import tests.support.resource.Support_Resources;
@@ -210,13 +209,12 @@
@SideEffect("Support_TestWebServer requires isolation.")
public void test_findResourceLjava_lang_String() throws Exception {
- int port = Support_PortManager.getNextPort();
File tmp = File.createTempFile("test", ".txt");
Support_TestWebServer server = new Support_TestWebServer();
try {
- server.initServer(port, tmp.getAbsolutePath(), "text/html");
+ int port = server.initServer(tmp.getAbsolutePath(), "text/html");
URL[] urls = { new URL("http://localhost:" + port + "/") };
ucl = new URLClassLoader(urls);
@@ -244,9 +242,8 @@
tempFile2.deleteOnExit();
Support_TestWebServer server = new Support_TestWebServer();
- int port = Support_PortManager.getNextPort();
try {
- server.initServer(port, false);
+ int port = server.initServer();
String tempPath1 = tempFile1.getParentFile().getAbsolutePath() + "/";
InputStream is = getClass().getResourceAsStream(
diff --git a/luni/src/test/java/libcore/java/net/URITest.java b/luni/src/test/java/libcore/java/net/URITest.java
index b37358c..04a7d2e 100644
--- a/luni/src/test/java/libcore/java/net/URITest.java
+++ b/luni/src/test/java/libcore/java/net/URITest.java
@@ -659,5 +659,13 @@
}
}
+ // http://code.google.com/p/android/issues/detail?id=37577
+ public void testUnderscore() throws Exception {
+ URI uri = new URI("http://a_b.c.d.net/");
+ assertEquals("a_b.c.d.net", uri.getAuthority());
+ // The RFC's don't permit underscores in hostnames, and neither does URI (unlike URL).
+ assertNull(uri.getHost());
+ }
+
// Adding a new test? Consider adding an equivalent test to URLTest.java
}
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index 347242a..762bac4 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -24,7 +24,9 @@
import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
+import dalvik.system.CloseGuard;
import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -76,6 +78,7 @@
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import junit.framework.TestCase;
+import libcore.java.lang.ref.FinalizationTester;
import libcore.java.security.TestKeyStore;
import libcore.javax.net.ssl.TestSSLContext;
import libcore.net.http.HttpResponseCache;
@@ -818,6 +821,50 @@
assertEquals(200, connection.getResponseCode());
}
+ public void testDisconnectAfterOnlyResponseCodeCausesNoCloseGuardWarning() throws IOException {
+ CloseGuardGuard guard = new CloseGuardGuard();
+ try {
+ server.enqueue(new MockResponse()
+ .setBody(gzip("ABCABCABC".getBytes("UTF-8")))
+ .addHeader("Content-Encoding: gzip"));
+ server.play();
+
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ assertEquals(200, connection.getResponseCode());
+ connection.disconnect();
+ connection = null;
+ assertFalse(guard.wasCloseGuardCalled());
+ } finally {
+ guard.close();
+ }
+ }
+
+ public static class CloseGuardGuard implements Closeable, CloseGuard.Reporter {
+ private final CloseGuard.Reporter oldReporter = CloseGuard.getReporter();
+
+ private AtomicBoolean closeGuardCalled = new AtomicBoolean();
+
+ public CloseGuardGuard() {
+ CloseGuard.setReporter(this);
+ }
+
+ @Override public void report(String message, Throwable allocationSite) {
+ oldReporter.report(message, allocationSite);
+ closeGuardCalled.set(true);
+ }
+
+ public boolean wasCloseGuardCalled() {
+ FinalizationTester.induceFinalization();
+ close();
+ return closeGuardCalled.get();
+ }
+
+ @Override public void close() {
+ CloseGuard.setReporter(oldReporter);
+ }
+
+ }
+
public void testDefaultRequestProperty() throws Exception {
URLConnection.setDefaultRequestProperty("X-testSetDefaultRequestProperty", "A");
assertNull(URLConnection.getDefaultRequestProperty("X-setDefaultRequestProperty"));
diff --git a/luni/src/test/java/libcore/java/net/URLTest.java b/luni/src/test/java/libcore/java/net/URLTest.java
index ced8314..962088e 100644
--- a/luni/src/test/java/libcore/java/net/URLTest.java
+++ b/luni/src/test/java/libcore/java/net/URLTest.java
@@ -686,5 +686,13 @@
assertEquals("re f", new URL("http://host/file?query#re f").getRef());
}
+ // http://code.google.com/p/android/issues/detail?id=37577
+ public void testUnderscore() throws Exception {
+ URL url = new URL("http://a_b.c.d.net/");
+ assertEquals("a_b.c.d.net", url.getAuthority());
+ // The RFC's don't permit underscores in hostnames, but URL accepts them (unlike URI).
+ assertEquals("a_b.c.d.net", url.getHost());
+ }
+
// Adding a new test? Consider adding an equivalent test to URITest.java
}
diff --git a/luni/src/test/java/libcore/java/nio/BufferTest.java b/luni/src/test/java/libcore/java/nio/BufferTest.java
index 06a8e94..2a895fc 100644
--- a/luni/src/test/java/libcore/java/nio/BufferTest.java
+++ b/luni/src/test/java/libcore/java/nio/BufferTest.java
@@ -675,4 +675,80 @@
}
assertFalse(bb.hasArray());
}
+
+ public void testBug6085292() {
+ ByteBuffer b = ByteBuffer.allocateDirect(1);
+
+ try {
+ b.asCharBuffer().get();
+ fail();
+ } catch (BufferUnderflowException expected) {
+ }
+ try {
+ b.asCharBuffer().get(0);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ assertTrue(expected.getMessage().contains("limit=0"));
+ }
+
+ try {
+ b.asDoubleBuffer().get();
+ fail();
+ } catch (BufferUnderflowException expected) {
+ }
+ try {
+ b.asDoubleBuffer().get(0);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ assertTrue(expected.getMessage().contains("limit=0"));
+ }
+
+ try {
+ b.asFloatBuffer().get();
+ fail();
+ } catch (BufferUnderflowException expected) {
+ }
+ try {
+ b.asFloatBuffer().get(0);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ assertTrue(expected.getMessage().contains("limit=0"));
+ }
+
+ try {
+ b.asIntBuffer().get();
+ fail();
+ } catch (BufferUnderflowException expected) {
+ }
+ try {
+ b.asIntBuffer().get(0);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ assertTrue(expected.getMessage().contains("limit=0"));
+ }
+
+ try {
+ b.asLongBuffer().get();
+ fail();
+ } catch (BufferUnderflowException expected) {
+ }
+ try {
+ b.asLongBuffer().get(0);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ assertTrue(expected.getMessage().contains("limit=0"));
+ }
+
+ try {
+ b.asShortBuffer().get();
+ fail();
+ } catch (BufferUnderflowException expected) {
+ }
+ try {
+ b.asShortBuffer().get(0);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ assertTrue(expected.getMessage().contains("limit=0"));
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/OldServerSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/OldServerSocketChannelTest.java
index fb63512..51f288a 100644
--- a/luni/src/test/java/libcore/java/nio/channels/OldServerSocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/OldServerSocketChannelTest.java
@@ -25,26 +25,17 @@
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import junit.framework.TestCase;
-import tests.support.Support_PortManager;
-/*
- * test for ServerSocketChannel
- */
public class OldServerSocketChannelTest extends TestCase {
private static final int TIME_UNIT = 200;
- private InetSocketAddress localAddr1;
-
private ServerSocketChannel serverChannel;
private SocketChannel clientChannel;
protected void setUp() throws Exception {
super.setUp();
- this.localAddr1 = new InetSocketAddress(
- "127.0.0.1", Support_PortManager
- .getNextPort());
this.serverChannel = ServerSocketChannel.open();
this.clientChannel = SocketChannel.open();
}
@@ -87,7 +78,7 @@
public void test_accept_Block_NoConnect_interrupt() throws IOException {
assertTrue(this.serverChannel.isBlocking());
ServerSocket gotSocket = this.serverChannel.socket();
- gotSocket.bind(localAddr1);
+ gotSocket.bind(null);
class MyThread extends Thread {
public String errMsg = null;
diff --git a/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
index 3987194..6560a7b 100644
--- a/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
@@ -35,11 +35,7 @@
import java.nio.channels.UnsupportedAddressTypeException;
import java.nio.channels.spi.SelectorProvider;
import junit.framework.TestCase;
-import tests.support.Support_PortManager;
-/**
- * Tests for SocketChannel and its default implementation.
- */
public class OldSocketChannelTest extends TestCase {
private static final int CAPACITY_NORMAL = 200;
@@ -58,11 +54,10 @@
protected void setUp() throws Exception {
super.setUp();
- this.localAddr1 = new InetSocketAddress("127.0.0.1",
- Support_PortManager.getNextPort());
this.channel1 = SocketChannel.open();
this.channel2 = SocketChannel.open();
- this.server1 = new ServerSocket(localAddr1.getPort());
+ this.server1 = new ServerSocket(0);
+ this.localAddr1 = (InetSocketAddress) server1.getLocalSocketAddress();
}
protected void tearDown() throws Exception {
diff --git a/luni/src/test/java/libcore/java/security/KeyStoreTest.java b/luni/src/test/java/libcore/java/security/KeyStoreTest.java
index 14d0987..15314c9 100644
--- a/luni/src/test/java/libcore/java/security/KeyStoreTest.java
+++ b/luni/src/test/java/libcore/java/security/KeyStoreTest.java
@@ -21,6 +21,7 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
@@ -50,7 +51,6 @@
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import junit.framework.TestCase;
-import libcore.io.IoUtils;
public class KeyStoreTest extends TestCase {
@@ -207,7 +207,7 @@
if (isReadOnly(ks)) {
try {
setPrivateKey(ks);
- fail();
+ fail(ks.toString());
} catch (UnsupportedOperationException e) {
}
return;
@@ -377,7 +377,7 @@
String type = KeyStore.getDefaultType();
try {
KeyStore.getInstance(null);
- fail();
+ fail(type);
} catch (NullPointerException expected) {
}
@@ -386,12 +386,12 @@
String providerName = StandardNames.SECURITY_PROVIDER_NAME;
try {
KeyStore.getInstance(null, (String)null);
- fail();
+ fail(type);
} catch (IllegalArgumentException expected) {
}
try {
KeyStore.getInstance(null, providerName);
- fail();
+ fail(type);
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != KeyStoreException.class) {
@@ -400,7 +400,7 @@
}
try {
KeyStore.getInstance(type, (String)null);
- fail();
+ fail(type);
} catch (IllegalArgumentException expected) {
}
assertNotNull(KeyStore.getInstance(type, providerName));
@@ -408,17 +408,17 @@
Provider provider = Security.getProvider(providerName);
try {
KeyStore.getInstance(null, (Provider)null);
- fail();
+ fail(type);
} catch (IllegalArgumentException expected) {
}
try {
KeyStore.getInstance(null, provider);
- fail();
+ fail(type);
} catch (NullPointerException expected) {
}
try {
KeyStore.getInstance(type, (Provider)null);
- fail();
+ fail(type);
} catch (IllegalArgumentException expected) {
}
assertNotNull(KeyStore.getInstance(type, provider));
@@ -457,7 +457,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.getKey(null, null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -468,7 +468,7 @@
// test odd inputs
try {
keyStore.getKey(null, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != IllegalArgumentException.class) {
@@ -477,7 +477,7 @@
}
try {
keyStore.getKey(null, PASSWORD_KEY);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != IllegalArgumentException.class
@@ -520,7 +520,7 @@
} else {
try {
keyStore.getKey(ALIAS_PRIVATE, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != UnrecoverableKeyException.class
&& e.getClass() != IllegalArgumentException.class) {
@@ -534,7 +534,7 @@
} else if (isSecretKeyEnabled(keyStore)) {
try {
keyStore.getKey(ALIAS_SECRET, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != UnrecoverableKeyException.class
&& e.getClass() != IllegalArgumentException.class) {
@@ -551,7 +551,7 @@
} else {
try {
keyStore.getKey(ALIAS_PRIVATE, PASSWORD_BAD);
- fail();
+ fail(keyStore.getType());
} catch (UnrecoverableKeyException expected) {
}
}
@@ -560,7 +560,7 @@
} else if (isSecretKeyEnabled(keyStore)) {
try {
keyStore.getKey(ALIAS_SECRET, PASSWORD_BAD);
- fail();
+ fail(keyStore.getType());
} catch (UnrecoverableKeyException expected) {
}
}
@@ -571,7 +571,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.getCertificateChain(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -581,7 +581,7 @@
// test odd inputs
try {
keyStore.getCertificateChain(null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != IllegalArgumentException.class) {
@@ -610,7 +610,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.getCertificate(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -620,7 +620,7 @@
// test odd inputs
try {
keyStore.getCertificate(null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != IllegalArgumentException.class) {
@@ -651,7 +651,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.getCreationDate(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -664,7 +664,7 @@
// test odd inputs
try {
keyStore.getCreationDate(null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
assertNull(keyStore.getCreationDate(""));
@@ -696,7 +696,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.setKeyEntry(null, null, null, null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -706,7 +706,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.setKeyEntry(null, null, null, null);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -715,7 +715,7 @@
// test odd inputs
try {
keyStore.setKeyEntry(null, null, null, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != KeyStoreException.class) {
@@ -724,7 +724,7 @@
}
try {
keyStore.setKeyEntry(null, null, PASSWORD_KEY, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != KeyStoreException.class) {
@@ -736,7 +736,7 @@
getPrivateKey().getPrivateKey(),
PASSWORD_KEY,
null);
- fail();
+ fail(keyStore.getType());
} catch (IllegalArgumentException expected) {
}
}
@@ -749,7 +749,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.setKeyEntry(ALIAS_SECRET, getSecretKey(), PASSWORD_KEY, null);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -764,7 +764,7 @@
} else {
try {
keyStore.setKeyEntry(ALIAS_SECRET, getSecretKey(), PASSWORD_KEY, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != KeyStoreException.class
&& e.getClass() != NullPointerException.class) {
@@ -820,7 +820,7 @@
getPrivateKey().getPrivateKey(),
null,
getPrivateKey().getCertificateChain());
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -839,7 +839,7 @@
getPrivateKey().getPrivateKey(),
null,
getPrivateKey().getCertificateChain());
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != UnrecoverableKeyException.class
&& e.getClass() != IllegalArgumentException.class
@@ -855,7 +855,7 @@
} else {
try {
keyStore.setKeyEntry(ALIAS_SECRET, getSecretKey(), null, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != UnrecoverableKeyException.class
&& e.getClass() != IllegalArgumentException.class
@@ -872,7 +872,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.setKeyEntry(null, null, null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -883,7 +883,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.setKeyEntry(null, null, null);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -892,7 +892,7 @@
// test odd inputs
try {
keyStore.setKeyEntry(null, null, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != IllegalArgumentException.class
@@ -920,7 +920,7 @@
if (isReadOnly(keyStore)) {
try {
setPrivateKeyBytes(keyStore);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -935,7 +935,7 @@
} else {
try {
keyStore.setKeyEntry(ALIAS_SECRET, getSecretKey().getEncoded(), null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -994,7 +994,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.setCertificateEntry(null, null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1005,7 +1005,7 @@
// test odd inputs
try {
keyStore.setCertificateEntry(null, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != KeyStoreException.class) {
@@ -1017,7 +1017,7 @@
try {
assertNull(keyStore.getCertificate(ALIAS_CERTIFICATE));
keyStore.setCertificateEntry(ALIAS_CERTIFICATE, null);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -1042,7 +1042,7 @@
} else {
try {
keyStore.setCertificateEntry(ALIAS_CERTIFICATE, null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1060,7 +1060,7 @@
if (isReadOnly(keyStore)) {
try {
setCertificate(keyStore);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -1104,7 +1104,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.deleteEntry(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1115,7 +1115,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.deleteEntry(null);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -1124,7 +1124,7 @@
// test odd inputs
try {
keyStore.deleteEntry(null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != KeyStoreException.class) {
@@ -1201,7 +1201,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.aliases();
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1245,7 +1245,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.containsAlias(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1255,7 +1255,7 @@
try {
keyStore.containsAlias(null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
@@ -1288,7 +1288,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.aliases();
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1330,7 +1330,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.isKeyEntry(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1340,7 +1340,7 @@
try {
keyStore.isKeyEntry(null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
@@ -1371,7 +1371,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.isCertificateEntry(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1382,7 +1382,7 @@
if (isCertificateEnabled(keyStore)) {
try {
keyStore.isCertificateEntry(null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
} else {
@@ -1415,7 +1415,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.getCertificateAlias(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1479,7 +1479,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.store(null, null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1490,7 +1490,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.store(out, null);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -1504,7 +1504,7 @@
try {
keyStore.store(out, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != IllegalArgumentException.class
&& e.getClass() != NullPointerException.class) {
@@ -1520,7 +1520,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.store(out, null);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException e) {
}
} else if (isNullPasswordAllowed(keyStore)) {
@@ -1529,7 +1529,7 @@
} else {
try {
keyStore.store(out, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != IllegalArgumentException.class
&& e.getClass() != NullPointerException.class) {
@@ -1545,7 +1545,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.store(out, PASSWORD_STORE);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException e) {
}
continue;
@@ -1560,7 +1560,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.store(out, PASSWORD_STORE);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException e) {
}
continue;
@@ -1574,7 +1574,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.store(null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1583,7 +1583,7 @@
keyStore.load(null, null);
try {
keyStore.store(null);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
assertFalse(isLoadStoreParameterSupported(keyStore));
} catch (IllegalArgumentException expected) {
@@ -1632,7 +1632,7 @@
return null;
}
});
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
}
@@ -1642,7 +1642,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.getEntry(null, null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
}
@@ -1653,12 +1653,12 @@
// test odd inputs
try {
keyStore.getEntry(null, null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
try {
keyStore.getEntry(null, PARAM_KEY);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
assertNull(keyStore.getEntry("", null));
@@ -1709,7 +1709,7 @@
} else {
try {
keyStore.getEntry(ALIAS_PRIVATE, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != UnrecoverableKeyException.class
&& e.getClass() != IllegalArgumentException.class) {
@@ -1722,7 +1722,7 @@
} else if (isSecretKeyEnabled(keyStore)) {
try {
keyStore.getEntry(ALIAS_SECRET, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != UnrecoverableKeyException.class
&& e.getClass() != IllegalArgumentException.class) {
@@ -1739,7 +1739,7 @@
} else {
try {
keyStore.getEntry(ALIAS_PRIVATE, PARAM_BAD);
- fail();
+ fail(keyStore.getType());
} catch (UnrecoverableKeyException expected) {
}
}
@@ -1748,19 +1748,22 @@
} else if (isSecretKeyEnabled(keyStore)) {
try {
keyStore.getEntry(ALIAS_SECRET, PARAM_BAD);
- fail();
+ fail(keyStore.getType());
} catch (UnrecoverableKeyException expected) {
}
}
}
}
+ public static class FakeProtectionParameter implements ProtectionParameter {
+ }
+
public void test_KeyStore_setEntry() throws Exception {
for (KeyStore keyStore : keyStores()) {
keyStore.load(null, null);
try {
keyStore.setEntry(null, null, null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
}
@@ -1768,10 +1771,20 @@
for (KeyStore keyStore : keyStores()) {
keyStore.load(null, null);
+ try {
+ keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), new FakeProtectionParameter());
+ fail("Should not accept unknown ProtectionParameter");
+ } catch (KeyStoreException expected) {
+ }
+ }
+
+ for (KeyStore keyStore : keyStores()) {
+ keyStore.load(null, null);
+
// test odd inputs
try {
keyStore.setEntry(null, null, null);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != KeyStoreException.class) {
@@ -1780,7 +1793,7 @@
}
try {
keyStore.setEntry(null, null, PARAM_KEY);
- fail();
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != NullPointerException.class
&& e.getClass() != KeyStoreException.class) {
@@ -1789,7 +1802,7 @@
}
try {
keyStore.setEntry("", null, PARAM_KEY);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
}
@@ -1802,7 +1815,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), PARAM_KEY);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -1817,7 +1830,7 @@
} else {
try {
keyStore.setKeyEntry(ALIAS_SECRET, getSecretKey(), PASSWORD_KEY, null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1832,7 +1845,7 @@
keyStore.setEntry(ALIAS_CERTIFICATE,
new TrustedCertificateEntry(getPrivateKey().getCertificate()),
null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1846,7 +1859,7 @@
} else {
try {
keyStore.setKeyEntry(ALIAS_UNICODE_SECRET, getSecretKey(), PASSWORD_KEY, null);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1931,20 +1944,33 @@
keyStore.load(null, null);
// test with null/non-null passwords
- try {
- keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), null);
- fail();
- } catch (Exception e) {
- if (e.getClass() != UnrecoverableKeyException.class
- && e.getClass() != IllegalArgumentException.class
- && e.getClass() != KeyStoreException.class) {
- throw e;
+ if (isReadOnly(keyStore)) {
+ try {
+ keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), null);
+ fail(keyStore.getType());
+ } catch (UnsupportedOperationException expected) {
}
- }
- if (isSecretKeyEnabled(keyStore)) {
try {
keyStore.setEntry(ALIAS_SECRET, new SecretKeyEntry(getSecretKey()), null);
- fail();
+ fail(keyStore.getType());
+ } catch (UnsupportedOperationException expected) {
+ }
+ try {
+ keyStore.setEntry(ALIAS_CERTIFICATE,
+ new TrustedCertificateEntry(getPrivateKey().getCertificate()),
+ null);
+ fail(keyStore.getType());
+ } catch (UnsupportedOperationException expected) {
+ }
+ continue;
+ }
+ if (isNullPasswordAllowed(keyStore) || isKeyPasswordIgnored(keyStore)) {
+ keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), null);
+ assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, null));
+ } else {
+ try {
+ keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), null);
+ fail(keyStore.getType());
} catch (Exception e) {
if (e.getClass() != UnrecoverableKeyException.class
&& e.getClass() != IllegalArgumentException.class
@@ -1953,15 +1979,22 @@
}
}
}
- if (isReadOnly(keyStore)) {
- try {
- keyStore.setEntry(ALIAS_CERTIFICATE,
- new TrustedCertificateEntry(getPrivateKey().getCertificate()),
- PARAM_KEY);
- fail();
- } catch (UnsupportedOperationException expected) {
+ if (isSecretKeyEnabled(keyStore)) {
+ if (isNullPasswordAllowed(keyStore) || isKeyPasswordIgnored(keyStore)) {
+ keyStore.setEntry(ALIAS_SECRET, new SecretKeyEntry(getSecretKey()), null);
+ assertSecretKey(keyStore.getKey(ALIAS_SECRET, null));
+ } else {
+ try {
+ keyStore.setEntry(ALIAS_SECRET, new SecretKeyEntry(getSecretKey()), null);
+ fail(keyStore.getType());
+ } catch (Exception e) {
+ if (e.getClass() != UnrecoverableKeyException.class
+ && e.getClass() != IllegalArgumentException.class
+ && e.getClass() != KeyStoreException.class) {
+ throw e;
+ }
+ }
}
- continue;
}
if (isCertificateEnabled(keyStore)) {
if (isNullPasswordAllowed(keyStore) || isKeyPasswordIgnored(keyStore)) {
@@ -1975,7 +2008,7 @@
new TrustedCertificateEntry(
getPrivateKey().getCertificate()),
PARAM_KEY);
- fail();
+ fail(keyStore.getType());
} catch (KeyStoreException expected) {
}
}
@@ -1987,7 +2020,7 @@
for (KeyStore keyStore : keyStores()) {
try {
keyStore.entryInstanceOf(null, null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
}
@@ -1997,17 +2030,17 @@
try {
keyStore.entryInstanceOf(null, null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
try {
keyStore.entryInstanceOf(null, Entry.class);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
try {
keyStore.entryInstanceOf("", null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
@@ -2082,7 +2115,7 @@
keyStore.load(null, null);
try {
Builder.newInstance(keyStore, null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
}
@@ -2092,7 +2125,7 @@
Builder.newInstance(keyStore.getType(),
keyStore.getProvider(),
null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
}
@@ -2103,7 +2136,7 @@
null,
null,
null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
try {
@@ -2111,7 +2144,7 @@
keyStore.getProvider(),
null,
null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
}
@@ -2121,13 +2154,13 @@
Builder builder = Builder.newInstance(keyStore, PARAM_STORE);
try {
builder.getProtectionParameter(null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
assertEquals(keyStore, builder.getKeyStore());
try {
builder.getProtectionParameter(null);
- fail();
+ fail(keyStore.getType());
} catch (NullPointerException expected) {
}
assertEquals(PARAM_STORE, builder.getProtectionParameter(""));
@@ -2143,7 +2176,7 @@
if (isReadOnly(keyStore)) {
try {
keyStore.store(os, PASSWORD_STORE);
- fail();
+ fail(keyStore.getType());
} catch (UnsupportedOperationException expected) {
}
continue;
@@ -2160,7 +2193,12 @@
assertEquals(PARAM_STORE, builder.getProtectionParameter(""));
assertEqualsKeyStores(file, PASSWORD_STORE, keyStore);
} finally {
- IoUtils.closeQuietly(os);
+ try {
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException ignored) {
+ }
file.delete();
}
}
@@ -2215,7 +2253,7 @@
// http://b/857840: want JKS key store
public void testDefaultKeystore() {
String type = KeyStore.getDefaultType();
- assertEquals("Default keystore type must be Bouncy Castle", "BKS", type);
+ assertEquals(StandardNames.KEY_STORE_ALGORITHM, type);
try {
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
@@ -2225,7 +2263,7 @@
}
try {
- KeyStore store = KeyStore.getInstance("BKS");
+ KeyStore store = KeyStore.getInstance(StandardNames.KEY_STORE_ALGORITHM);
assertNotNull("Keystore must not be null", store);
} catch (Exception ex) {
throw new RuntimeException(ex);
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index 695908b..78608d0 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -17,14 +17,20 @@
package libcore.java.security;
import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.SecureRandomSpi;
+import java.security.Security;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.SecureRandomSpi;
import java.security.Security;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Map.Entry;
+import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -195,4 +201,48 @@
}
}
}
+
+ /**
+ * http://code.google.com/p/android/issues/detail?id=21449
+ */
+ public void testSecureRandomImplementationOrder() {
+ Provider srp = new SRProvider();
+ try {
+ int position = Security.insertProviderAt(srp, 1); // first is one, not zero
+ assertEquals(1, position);
+ SecureRandom sr = new SecureRandom();
+ if (!sr.getAlgorithm().equals("SecureRandom1")) {
+ throw new IllegalStateException("Expected SecureRandom1");
+ }
+ } finally {
+ Security.removeProvider(srp.getName());
+ }
+ }
+
+ public static class SRProvider extends Provider {
+
+ SRProvider() {
+ super("SRProvider", 1.42, "SecureRandom Provider");
+ put("SecureRandom.SecureRandom1", SecureRandom1.class.getName());
+ put("SecureRandom.SecureRandom2", SecureRandom2.class.getName());
+ put("SecureRandom.SecureRandom3", SecureRandom3.class.getName());
+ }
+ }
+
+ public static abstract class AbstractSecureRandom extends SecureRandomSpi {
+ protected void engineSetSeed(byte[] seed) {
+ throw new UnsupportedOperationException();
+ }
+ protected void engineNextBytes(byte[] bytes) {
+ throw new UnsupportedOperationException();
+ }
+ protected byte[] engineGenerateSeed(int numBytes) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public static class SecureRandom1 extends AbstractSecureRandom {}
+ public static class SecureRandom2 extends AbstractSecureRandom {}
+ public static class SecureRandom3 extends AbstractSecureRandom {}
+
}
diff --git a/luni/src/test/java/libcore/java/security/SecureRandomTest.java b/luni/src/test/java/libcore/java/security/SecureRandomTest.java
new file mode 100644
index 0000000..8199120
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/SecureRandomTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.security;
+
+import org.apache.harmony.xnet.provider.jsse.OpenSSLProvider;
+
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+public class SecureRandomTest extends TestCase {
+ private static final Class<? extends Provider> EXPECTED_PROVIDER = OpenSSLProvider.class;
+
+ private static final byte[] STATIC_SEED_BYTES = new byte[] {
+ 0x0A, (byte) 0xA0, 0x01, 0x10, (byte) 0xFF, (byte) 0xF0, 0x0F
+ };
+
+ private static final long STATIC_SEED_LONG = 8506210602917522860L;
+
+ public void test_getInstance() throws Exception {
+ Provider[] providers = Security.getProviders();
+ for (Provider provider : providers) {
+ Set<Provider.Service> services = provider.getServices();
+ for (Provider.Service service : services) {
+ String type = service.getType();
+ if (!type.equals("SecureRandom")) {
+ continue;
+ }
+
+ String algorithm = service.getAlgorithm();
+ try {
+ SecureRandom sr1 = SecureRandom.getInstance(algorithm);
+ assertEquals(algorithm, sr1.getAlgorithm());
+ test_SecureRandom(sr1);
+
+ // SecureRandom.getInstance(String, Provider)
+ SecureRandom sr2 = SecureRandom.getInstance(algorithm, provider);
+ assertEquals(algorithm, sr2.getAlgorithm());
+ assertEquals(provider, sr2.getProvider());
+ test_SecureRandom(sr2);
+
+ // SecureRandom.getInstance(String, String)
+ SecureRandom sr3 = SecureRandom.getInstance(algorithm, provider.getName());
+ assertEquals(algorithm, sr3.getAlgorithm());
+ assertEquals(provider, sr3.getProvider());
+ test_SecureRandom(sr3);
+
+ System.out.println("SecureRandomTest " + algorithm + " and provider "
+ + provider.getName());
+ } catch (Exception e) {
+ throw new Exception("Problem testing SecureRandom." + algorithm
+ + ", provider: " + provider.getName(), e);
+ }
+ }
+ }
+ }
+
+ private void test_SecureRandom(SecureRandom sr) throws Exception {
+ byte[] out1 = new byte[20];
+ byte[] out2 = new byte[20];
+
+ sr.nextBytes(out1);
+ sr.nextBytes(out2);
+ assertFalse(Arrays.equals(out1, out2));
+
+ byte[] seed1 = sr.generateSeed(20);
+ byte[] seed2 = sr.generateSeed(20);
+ assertFalse(Arrays.equals(seed1, seed2));
+
+ sr.setSeed(STATIC_SEED_BYTES);
+ sr.nextBytes(out1);
+ sr.nextBytes(out2);
+ assertFalse(Arrays.equals(out1, out2));
+
+ sr.setSeed(STATIC_SEED_LONG);
+ sr.nextBytes(out1);
+ sr.nextBytes(out2);
+ assertFalse(Arrays.equals(out1, out2));
+ }
+
+ public void testGetCommonInstances_Success() throws Exception {
+ SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
+ assertNotNull(sr);
+ assertEquals(EXPECTED_PROVIDER, sr.getProvider().getClass());
+ }
+
+ public void testNewConstructors_Success() throws Exception {
+ SecureRandom sr1 = new SecureRandom();
+ assertEquals(EXPECTED_PROVIDER, sr1.getProvider().getClass());
+ test_SecureRandom(sr1);
+
+ SecureRandom sr2 = new SecureRandom(STATIC_SEED_BYTES);
+ assertEquals(EXPECTED_PROVIDER, sr2.getProvider().getClass());
+ test_SecureRandom(sr2);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index 2cd9982..92b5513 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -26,6 +26,7 @@
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
+import java.security.SignatureException;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
@@ -123,9 +124,21 @@
sig.update(DATA);
assertTrue(sig.getAlgorithm(), sig.verify(signature));
- // Calling Signature.verify a second time should not throw
- // http://code.google.com/p/android/issues/detail?id=34933
- sig.verify(signature);
+ /*
+ * The RI appears to clear out the input data in RawDSA while calling
+ * verify a second time.
+ */
+ if (StandardNames.IS_RI && "NONEwithDSA".equalsIgnoreCase(sig.getAlgorithm())) {
+ try {
+ sig.verify(signature);
+ fail("Expected RI to have a NONEwithDSA bug");
+ } catch (SignatureException bug) {
+ }
+ } else {
+ // Calling Signature.verify a second time should not throw
+ // http://code.google.com/p/android/issues/detail?id=34933
+ sig.verify(signature);
+ }
}
private static final byte[] PK_BYTES = hexToBytes(
@@ -326,6 +339,7 @@
(byte) 0x39,
});
+ /* Test data is: "Android.\n" */
private static final byte[] Vector1Data = new byte[] {
(byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x2e,
(byte) 0x0a,
@@ -550,11 +564,61 @@
(byte) 0x02, (byte) 0xaf, (byte) 0x8f, (byte) 0x59, (byte) 0xe5, (byte) 0x67, (byte) 0x25, (byte) 0x00,
};
+ /*
+ * openssl rsautl -raw -sign -inkey rsa.key | recode ../x1 | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] NONEwithRSA_Vector1Signature = new byte[] {
+ (byte) 0x35, (byte) 0x43, (byte) 0x38, (byte) 0x44, (byte) 0xAD, (byte) 0x3F,
+ (byte) 0x97, (byte) 0x02, (byte) 0xFB, (byte) 0x59, (byte) 0x1F, (byte) 0x4A,
+ (byte) 0x2B, (byte) 0xB9, (byte) 0x06, (byte) 0xEC, (byte) 0x66, (byte) 0xE6,
+ (byte) 0xD2, (byte) 0xC5, (byte) 0x8B, (byte) 0x7B, (byte) 0xE3, (byte) 0x18,
+ (byte) 0xBF, (byte) 0x07, (byte) 0xD6, (byte) 0x01, (byte) 0xF9, (byte) 0xD9,
+ (byte) 0x89, (byte) 0xC4, (byte) 0xDB, (byte) 0x00, (byte) 0x68, (byte) 0xFF,
+ (byte) 0x9B, (byte) 0x43, (byte) 0x90, (byte) 0xF2, (byte) 0xDB, (byte) 0x83,
+ (byte) 0xF4, (byte) 0x7E, (byte) 0xC6, (byte) 0x81, (byte) 0x01, (byte) 0x3A,
+ (byte) 0x0B, (byte) 0xE5, (byte) 0xED, (byte) 0x08, (byte) 0x73, (byte) 0x3E,
+ (byte) 0xE1, (byte) 0x3F, (byte) 0xDF, (byte) 0x1F, (byte) 0x07, (byte) 0x6D,
+ (byte) 0x22, (byte) 0x8D, (byte) 0xCC, (byte) 0x4E, (byte) 0xE3, (byte) 0x9A,
+ (byte) 0xBC, (byte) 0xCC, (byte) 0x8F, (byte) 0x9E, (byte) 0x9B, (byte) 0x02,
+ (byte) 0x48, (byte) 0x00, (byte) 0xAC, (byte) 0x9F, (byte) 0xA4, (byte) 0x8F,
+ (byte) 0x87, (byte) 0xA1, (byte) 0xA8, (byte) 0xE6, (byte) 0x9D, (byte) 0xCD,
+ (byte) 0x8B, (byte) 0x05, (byte) 0xE9, (byte) 0xD2, (byte) 0x05, (byte) 0x8D,
+ (byte) 0xC9, (byte) 0x95, (byte) 0x16, (byte) 0xD0, (byte) 0xCD, (byte) 0x43,
+ (byte) 0x25, (byte) 0x8A, (byte) 0x11, (byte) 0x46, (byte) 0xD7, (byte) 0x74,
+ (byte) 0x4C, (byte) 0xCF, (byte) 0x58, (byte) 0xF9, (byte) 0xA1, (byte) 0x30,
+ (byte) 0x84, (byte) 0x52, (byte) 0xC9, (byte) 0x01, (byte) 0x5F, (byte) 0x24,
+ (byte) 0x4C, (byte) 0xB1, (byte) 0x9F, (byte) 0x7D, (byte) 0x12, (byte) 0x38,
+ (byte) 0x27, (byte) 0x0F, (byte) 0x5E, (byte) 0xFF, (byte) 0xE0, (byte) 0x55,
+ (byte) 0x8B, (byte) 0xA3, (byte) 0xAD, (byte) 0x60, (byte) 0x35, (byte) 0x83,
+ (byte) 0x58, (byte) 0xAF, (byte) 0x99, (byte) 0xDE, (byte) 0x3F, (byte) 0x5D,
+ (byte) 0x80, (byte) 0x80, (byte) 0xFF, (byte) 0x9B, (byte) 0xDE, (byte) 0x5C,
+ (byte) 0xAB, (byte) 0x97, (byte) 0x43, (byte) 0x64, (byte) 0xD9, (byte) 0x9F,
+ (byte) 0xFB, (byte) 0x67, (byte) 0x65, (byte) 0xA5, (byte) 0x99, (byte) 0xE7,
+ (byte) 0xE6, (byte) 0xEB, (byte) 0x05, (byte) 0x95, (byte) 0xFC, (byte) 0x46,
+ (byte) 0x28, (byte) 0x4B, (byte) 0xD8, (byte) 0x8C, (byte) 0xF5, (byte) 0x0A,
+ (byte) 0xEB, (byte) 0x1F, (byte) 0x30, (byte) 0xEA, (byte) 0xE7, (byte) 0x67,
+ (byte) 0x11, (byte) 0x25, (byte) 0xF0, (byte) 0x44, (byte) 0x75, (byte) 0x74,
+ (byte) 0x94, (byte) 0x06, (byte) 0x78, (byte) 0xD0, (byte) 0x21, (byte) 0xF4,
+ (byte) 0x3F, (byte) 0xC8, (byte) 0xC4, (byte) 0x4A, (byte) 0x57, (byte) 0xBE,
+ (byte) 0x02, (byte) 0x3C, (byte) 0x93, (byte) 0xF6, (byte) 0x95, (byte) 0xFB,
+ (byte) 0xD1, (byte) 0x77, (byte) 0x8B, (byte) 0x43, (byte) 0xF0, (byte) 0xB9,
+ (byte) 0x7D, (byte) 0xE0, (byte) 0x32, (byte) 0xE1, (byte) 0x72, (byte) 0xB5,
+ (byte) 0x62, (byte) 0x3F, (byte) 0x86, (byte) 0xC3, (byte) 0xD4, (byte) 0x5F,
+ (byte) 0x5E, (byte) 0x54, (byte) 0x1B, (byte) 0x5B, (byte) 0xE6, (byte) 0x74,
+ (byte) 0xA1, (byte) 0x0B, (byte) 0xE5, (byte) 0x18, (byte) 0xD2, (byte) 0x4F,
+ (byte) 0x93, (byte) 0xF3, (byte) 0x09, (byte) 0x58, (byte) 0xCE, (byte) 0xF0,
+ (byte) 0xA3, (byte) 0x61, (byte) 0xE4, (byte) 0x6E, (byte) 0x46, (byte) 0x45,
+ (byte) 0x89, (byte) 0x50, (byte) 0xBD, (byte) 0x03, (byte) 0x3F, (byte) 0x38,
+ (byte) 0xDA, (byte) 0x5D, (byte) 0xD0, (byte) 0x1B, (byte) 0x1F, (byte) 0xB1,
+ (byte) 0xEE, (byte) 0x89, (byte) 0x59, (byte) 0xC5,
+ };
+
public void testGetCommonInstances_Success() throws Exception {
assertNotNull(Signature.getInstance("SHA1withRSA"));
assertNotNull(Signature.getInstance("SHA256withRSA"));
assertNotNull(Signature.getInstance("SHA384withRSA"));
assertNotNull(Signature.getInstance("SHA512withRSA"));
+ assertNotNull(Signature.getInstance("NONEwithRSA"));
assertNotNull(Signature.getInstance("MD5withRSA"));
assertNotNull(Signature.getInstance("SHA1withDSA"));
}
@@ -732,8 +796,7 @@
try {
sig.initSign(privKey);
fail("Should throw error when private exponent is not available");
- } catch (InvalidKeyException e) {
- // success
+ } catch (InvalidKeyException expected) {
}
}
@@ -757,8 +820,7 @@
try {
sig.initSign(privKey);
fail("Should throw error when modulus is not available");
- } catch (InvalidKeyException e) {
- // success
+ } catch (InvalidKeyException expected) {
}
}
@@ -781,8 +843,7 @@
try {
sig.initSign(privKey);
fail("Should throw error when key is empty");
- } catch (InvalidKeyException e) {
- // success
+ } catch (InvalidKeyException expected) {
}
}
@@ -902,6 +963,165 @@
assertTrue("Signature must verify correctly", sig.verify(signature));
}
+ public void testSign_NONEwithRSA_Key_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+ PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Signature sig = Signature.getInstance("NONEwithRSA");
+ sig.initSign(privKey);
+ sig.update(Vector1Data);
+
+ byte[] signature = sig.sign();
+ assertNotNull("Signature must not be null", signature);
+ assertTrue("Signature should match expected",
+ Arrays.equals(signature, NONEwithRSA_Vector1Signature));
+
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ PublicKey pubKey = kf.generatePublic(pubKeySpec);
+ sig.initVerify(pubKey);
+ sig.update(Vector1Data);
+ assertTrue("Signature must verify correctly", sig.verify(signature));
+ }
+
+ public void testVerify_NONEwithRSA_Key_WrongSignature_Failure() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+ Signature sig = Signature.getInstance("NONEwithRSA");
+ sig.initVerify(pubKey);
+ sig.update(Vector1Data);
+ assertFalse("Invalid signature must not verify", sig.verify("Invalid".getBytes()));
+ }
+
+ public void testSign_NONEwithRSA_Key_DataTooLarge_Failure() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+ PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Signature sig = Signature.getInstance("NONEwithRSA");
+ sig.initSign(privKey);
+
+ final int oneTooBig = RSA_2048_modulus.bitLength() - 10;
+ for (int i = 0; i < oneTooBig; i++) {
+ sig.update((byte) i);
+ }
+
+ try {
+ sig.sign();
+ fail("Should throw exception when data is too large");
+ } catch (SignatureException expected) {
+ }
+ }
+
+ public void testSign_NONEwithRSA_Key_DataTooLarge_SingleByte_Failure() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+ PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Signature sig = Signature.getInstance("NONEwithRSA");
+ sig.initSign(privKey);
+
+ // This should make it two bytes too big.
+ final int oneTooBig = RSA_2048_modulus.bitLength() - 10;
+ for (int i = 0; i < oneTooBig; i++) {
+ sig.update((byte) i);
+ }
+
+ try {
+ sig.sign();
+ fail("Should throw exception when data is too large");
+ } catch (SignatureException expected) {
+ }
+ }
+
+ public void testVerify_NONEwithRSA_Key_DataTooLarge_Failure() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+ Signature sig = Signature.getInstance("NONEwithRSA");
+ sig.initVerify(pubKey);
+
+ // This should make it one bytes too big.
+ final int oneTooBig = RSA_2048_modulus.bitLength() + 1;
+ final byte[] vector = new byte[oneTooBig];
+ for (int i = 0; i < oneTooBig; i++) {
+ vector[i] = (byte) Vector1Data[i % Vector1Data.length];
+ }
+ sig.update(vector);
+
+ assertFalse("Should not verify when signature is too large",
+ sig.verify(NONEwithRSA_Vector1Signature));
+ }
+
+ public void testVerify_NONEwithRSA_Key_DataTooLarge_SingleByte_Failure() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+ Signature sig = Signature.getInstance("NONEwithRSA");
+ sig.initVerify(pubKey);
+
+ // This should make it twice as big as it should be.
+ final int tooBig = RSA_2048_modulus.bitLength() * 2;
+ for (int i = 0; i < tooBig; i++) {
+ sig.update((byte) Vector1Data[i % Vector1Data.length]);
+ }
+
+ assertFalse("Should not verify when signature is too large",
+ sig.verify(NONEwithRSA_Vector1Signature));
+ }
+
+ public void testVerify_NONEwithRSA_Key_SignatureTooSmall_Failure() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+ Signature sig = Signature.getInstance("NONEwithRSA");
+ sig.initVerify(pubKey);
+ sig.update(Vector1Data);
+
+ assertFalse("Invalid signature should not verify", sig.verify("Invalid sig".getBytes()));
+ }
+
+ public void testVerify_NONEwithRSA_Key_SignatureTooLarge_Failure() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+ Signature sig = Signature.getInstance("NONEwithRSA");
+ sig.initVerify(pubKey);
+ sig.update(Vector1Data);
+
+ byte[] invalidSignature = new byte[NONEwithRSA_Vector1Signature.length * 2];
+ System.arraycopy(NONEwithRSA_Vector1Signature, 0, invalidSignature, 0,
+ NONEwithRSA_Vector1Signature.length);
+ System.arraycopy(NONEwithRSA_Vector1Signature, 0, invalidSignature,
+ NONEwithRSA_Vector1Signature.length, NONEwithRSA_Vector1Signature.length);
+
+ try {
+ sig.verify(invalidSignature);
+ fail("Should throw exception when signature is too large");
+ } catch (SignatureException expected) {
+ }
+ }
+
/*
* These tests were generated with this DSA private key:
*
diff --git a/luni/src/test/java/libcore/java/text/OldDateFormatTest.java b/luni/src/test/java/libcore/java/text/OldDateFormatTest.java
index 6b3885c..df388d3 100644
--- a/luni/src/test/java/libcore/java/text/OldDateFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/OldDateFormatTest.java
@@ -90,7 +90,7 @@
DateFormat.SHORT, DateFormat.SHORT, Locale.US);
Date current = new Date();
String dtf = format.format(current);
- SimpleDateFormat sdf = new SimpleDateFormat("M/d/yy h:mm a");
+ SimpleDateFormat sdf = new SimpleDateFormat("M/d/yy h:mm a", Locale.US);
assertTrue("Incorrect date format", sdf.format(current).equals(dtf));
} catch (Exception e) {
fail("Unexpected exception " + e.toString());
@@ -110,7 +110,7 @@
StringBuffer toAppend = new StringBuffer();
FieldPosition fp = new FieldPosition(DateFormat.YEAR_FIELD);
StringBuffer sb = format.format(current, toAppend, fp);
- SimpleDateFormat sdf = new SimpleDateFormat("M/d/yy h:mm a");
+ SimpleDateFormat sdf = new SimpleDateFormat("M/d/yy h:mm a", Locale.US);
assertTrue("Incorrect date format", sdf.format(current).equals(
sb.toString()));
assertTrue("Incorrect beginIndex of filed position", fp
diff --git a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
index c6296ff..cd54d1e 100644
--- a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
@@ -51,6 +51,7 @@
// The RI fails this test because this is an ICU-compatible Android extension.
// Necessary for correct localization in various languages (http://b/2633414).
public void testStandAloneNames() throws Exception {
+ TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
Locale en = Locale.ENGLISH;
Locale pl = new Locale("pl");
Locale ru = new Locale("ru");
@@ -77,7 +78,7 @@
}
public void test2038() {
- SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy");
+ SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
assertEquals("Sun Nov 24 17:31:44 1833",
@@ -175,14 +176,14 @@
* longer use their DST zone but we should continue to parse it properly.
*/
public void testObsoleteDstZoneName() throws Exception {
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz");
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US);
Date normal = format.parse("1970-01-01T00:00 EET");
Date dst = format.parse("1970-01-01T00:00 EEST");
assertEquals(60 * 60 * 1000, normal.getTime() - dst.getTime());
}
public void testDstZoneNameWithNonDstTimestamp() throws Exception {
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz");
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US);
Calendar calendar = new GregorianCalendar(AMERICA_LOS_ANGELES);
calendar.setTime(format.parse("2011-06-21T10:00 Pacific Standard Time")); // 18:00 GMT-8
assertEquals(11, calendar.get(Calendar.HOUR_OF_DAY)); // 18:00 GMT-7
@@ -190,7 +191,7 @@
}
public void testNonDstZoneNameWithDstTimestamp() throws Exception {
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz");
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US);
Calendar calendar = new GregorianCalendar(AMERICA_LOS_ANGELES);
calendar.setTime(format.parse("2010-12-21T10:00 Pacific Daylight Time")); // 17:00 GMT-7
assertEquals(9, calendar.get(Calendar.HOUR_OF_DAY)); // 17:00 GMT-8
@@ -199,7 +200,7 @@
// http://b/4723412
public void testDstZoneWithNonDstTimestampForNonHourDstZone() throws Exception {
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz");
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US);
Calendar calendar = new GregorianCalendar(AUSTRALIA_LORD_HOWE);
calendar.setTime(format.parse("2011-06-21T20:00 Lord Howe Daylight Time")); // 9:00 GMT+11
assertEquals(19, calendar.get(Calendar.HOUR_OF_DAY)); // 9:00 GMT+10:30
@@ -207,7 +208,7 @@
}
public void testNonDstZoneWithDstTimestampForNonHourDstZone() throws Exception {
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz");
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US);
Calendar calendar = new GregorianCalendar(AUSTRALIA_LORD_HOWE);
calendar.setTime(format.parse("2010-12-21T19:30 Lord Howe Standard Time")); //9:00 GMT+10:30
assertEquals(20, calendar.get(Calendar.HOUR_OF_DAY)); // 9:00 GMT+11:00
@@ -227,4 +228,23 @@
new SimpleDateFormat("z", Locale.FRANCE).parse("UTC");
new SimpleDateFormat("z", Locale.US).parse("UTC");
}
+
+ // http://code.google.com/p/android/issues/detail?id=36689
+ public void testParseArabic() throws Exception {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", new Locale("ar", "EG"));
+ sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
+
+ // Can we parse an ASCII-formatted date in an Arabic locale?
+ Date d = sdf.parse("2012-08-29 10:02:45");
+ assertEquals(1346259765000L, d.getTime());
+
+ // Can we format a date correctly in an Arabic locale?
+ String formatted = sdf.format(d);
+ assertEquals("٢٠١٢-٠٨-٢٩ ١٠:٠٢:٤٥", formatted);
+
+ // Can we parse the Arabic-formatted date in an Arabic locale, and get the same date
+ // we started with?
+ Date d2 = sdf.parse(formatted);
+ assertEquals(d, d2);
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java b/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
index 547b70a..8fbe5ff 100644
--- a/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
@@ -31,7 +31,6 @@
* resource bundles.
*/
public class OldAndroidLocaleTest extends TestCase {
-
// Test basic Locale infrastructure.
public void testLocale() throws Exception {
Locale locale = new Locale("en");
@@ -47,49 +46,6 @@
assertEquals("en_US_POSIX", locale.toString());
}
- /*
- * Tests some must-have locales. TODO: Add back "de". See discussion
- * immediately below this method.
- */
- public void testResourceBundles() throws Exception {
- Locale eng = new Locale("en", "US");
- DateFormatSymbols engSymbols = new DateFormatSymbols(eng);
-
- //Locale deu = new Locale("de", "DE");
- //DateFormatSymbols deuSymbols = new DateFormatSymbols(deu);
-
- TimeZone berlin = TimeZone.getTimeZone("Europe/Berlin");
-
- assertEquals("January", engSymbols.getMonths()[0]);
- //assertEquals("Januar", deuSymbols.getMonths()[0]);
-
- assertEquals("Sunday", engSymbols.getWeekdays()[Calendar.SUNDAY]);
- //assertEquals("Sonntag", deuSymbols.getWeekdays()[Calendar.SUNDAY]);
-
- assertEquals("Central European Time",
- berlin.getDisplayName(false, TimeZone.LONG, eng));
- assertEquals("Central European Summer Time",
- berlin.getDisplayName(true, TimeZone.LONG, eng));
-
- //assertEquals("Mitteleurop\u00E4ische Zeit",
- // berlin.getDisplayName(false, TimeZone.LONG, deu));
- //assertEquals("Mitteleurop\u00E4ische Sommerzeit",
- // berlin.getDisplayName(true, TimeZone.LONG, deu));
-
- assertTrue(engSymbols.getZoneStrings().length > 100);
- }
-
- /*
- * Disabled version of the above test. The version above omits
- * checks for stuff in the "de" locale, because we stripped that
- * out as part of the flash reduction effort (so that we could
- * still ship on Dream). We expect to have a baseline target that
- * includes a large enough system partition to include "de"
- * immediately after the last official release for Dream (whenever
- * that may be).
- *
- // Test some must-have locales.
- @LargeTest
public void testResourceBundles() throws Exception {
Locale eng = new Locale("en", "US");
DateFormatSymbols engSymbols = new DateFormatSymbols(eng);
@@ -117,7 +73,6 @@
assertTrue(engSymbols.getZoneStrings().length > 100);
}
- */
// This one makes sure we have all necessary locales installed.
// Suppress this flaky test for now.
diff --git a/luni/src/test/java/libcore/java/util/OldScannerTest.java b/luni/src/test/java/libcore/java/util/OldScannerTest.java
index b51cfbc..9bac1f4 100644
--- a/luni/src/test/java/libcore/java/util/OldScannerTest.java
+++ b/luni/src/test/java/libcore/java/util/OldScannerTest.java
@@ -28,7 +28,6 @@
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import junit.framework.TestCase;
-import tests.support.Support_PortManager;
public final class OldScannerTest extends TestCase {
@@ -492,15 +491,12 @@
assertEquals(102400, matchResult.end());
}
- public void test_Constructor_LReadableByteChannel()
- throws IOException {
- InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1",
- Support_PortManager.getNextPort());
+ public void test_Constructor_LReadableByteChannel() throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(localAddr);
+ ssc.socket().bind(null);
SocketChannel sc = SocketChannel.open();
- sc.connect(localAddr);
+ sc.connect(ssc.socket().getLocalSocketAddress());
sc.configureBlocking(false);
assertFalse(sc.isBlocking());
diff --git a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
index 240403e..4ed89de 100644
--- a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
@@ -83,6 +83,7 @@
}
public void test_getDisplayName() {
+ Locale.setDefault(Locale.US);
TimeZone tz = TimeZone.getTimeZone("GMT-6");
assertEquals("GMT-06:00", tz.getDisplayName());
tz = TimeZone.getTimeZone("America/Los_Angeles");
@@ -99,6 +100,7 @@
}
public void test_getDisplayNameZI() {
+ Locale.setDefault(Locale.US);
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
assertEquals("PST", tz.getDisplayName(false, 0));
assertEquals("Pacific Daylight Time", tz.getDisplayName(true, 1));
@@ -150,15 +152,4 @@
tz.setID("New ID for GMT-6");
assertEquals("New ID for GMT-6", tz.getID());
}
-
- Locale loc = null;
-
- protected void setUp() {
- loc = Locale.getDefault();
- Locale.setDefault(Locale.US);
- }
-
- protected void tearDown() {
- Locale.setDefault(loc);
- }
}
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index d94b017..6e8b8d8 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -17,12 +17,14 @@
package libcore.java.util;
import java.text.SimpleDateFormat;
+import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
-import java.util.TimeZone;
import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+import junit.framework.TestCase;
-public class TimeZoneTest extends junit.framework.TestCase {
+public class TimeZoneTest extends TestCase {
// http://code.google.com/p/android/issues/detail?id=877
public void test_useDaylightTime_Taiwan() {
TimeZone asiaTaipei = TimeZone.getTimeZone("Asia/Taipei");
@@ -144,8 +146,8 @@
// http://code.google.com/p/android/issues/detail?id=11918
public void testHasSameRules() throws Exception {
- TimeZone denver = TimeZone.getTimeZone ("America/Denver") ;
- TimeZone phoenix = TimeZone.getTimeZone ("America/Phoenix") ;
+ TimeZone denver = TimeZone.getTimeZone("America/Denver");
+ TimeZone phoenix = TimeZone.getTimeZone("America/Phoenix");
assertFalse(denver.hasSameRules(phoenix));
}
@@ -157,4 +159,56 @@
} catch (NullPointerException expected) {
}
}
+
+ // http://b.corp.google.com/issue?id=6556561
+ public void testCustomZoneIds() throws Exception {
+ // These are all okay (and equivalent).
+ assertEquals("GMT+05:00", TimeZone.getTimeZone("GMT+05:00").getID());
+ assertEquals("GMT+05:00", TimeZone.getTimeZone("GMT+5:00").getID());
+ assertEquals("GMT+05:00", TimeZone.getTimeZone("GMT+0500").getID());
+ assertEquals("GMT+05:00", TimeZone.getTimeZone("GMT+500").getID());
+ assertEquals("GMT+05:00", TimeZone.getTimeZone("GMT+5").getID());
+ // These aren't.
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5.5").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5:5").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5:0").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5:005").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5:000").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+005:00").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+05:99").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+28:00").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+05:00.00").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+05:00:00").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5:").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+junk").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5junk").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5:junk").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("GMT+5:00junk").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("junkGMT+5:00").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("junk").getID());
+ assertEquals("GMT", TimeZone.getTimeZone("gmt+5:00").getID());
+ }
+
+ public void test_getDSTSavings() throws Exception {
+ assertEquals(0, TimeZone.getTimeZone("UTC").getDSTSavings());
+ assertEquals(3600000, TimeZone.getTimeZone("America/Los_Angeles").getDSTSavings());
+ assertEquals(1800000, TimeZone.getTimeZone("Australia/Lord_Howe").getDSTSavings());
+ }
+
+ public void testSimpleTimeZoneDoesNotCallOverrideableMethodsFromConstructor() {
+ new SimpleTimeZone(0, "X", Calendar.MARCH, 1, 1, 1, Calendar.SEPTEMBER, 1, 1, 1) {
+ @Override public void setStartRule(int m, int d, int dow, int time) {
+ fail();
+ }
+ @Override public void setStartRule(int m, int d, int dow, int time, boolean after) {
+ fail();
+ }
+ @Override public void setEndRule(int m, int d, int dow, int time) {
+ fail();
+ }
+ @Override public void setEndRule(int m, int d, int dow, int time, boolean after) {
+ fail();
+ }
+ };
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java b/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java
index 450b8d9..07d99e9 100644
--- a/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java
+++ b/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java
@@ -247,19 +247,36 @@
}
}
- // Starting index out of region
+ String string3 = "Brave new world";
Pattern pat3 = Pattern.compile("new");
- Matcher mat3 = pat3.matcher("Brave new world");
+ Matcher mat3 = pat3.matcher(string3);
- assertTrue(mat3.find(-1));
+ // find(int) throws for out of range indexes.
+ try {
+ mat3.find(-1);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ assertFalse(mat3.find(string3.length()));
+ try {
+ mat3.find(string3.length() + 1);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ }
+
assertTrue(mat3.find(6));
assertFalse(mat3.find(7));
mat3.region(7, 10);
+ assertFalse(mat3.find()); // No "new" in the region.
- assertFalse(mat3.find(3));
- assertFalse(mat3.find(6));
- assertFalse(mat3.find(7));
+ assertTrue(mat3.find(3)); // find(int) ignores the region.
+ assertTrue(mat3.find(6)); // find(int) ignores the region.
+ assertFalse(mat3.find(7)); // No "new" >= 7.
+
+ mat3.region(1, 4);
+ assertFalse(mat3.find()); // No "new" in the region.
+ assertTrue(mat3.find(5)); // find(int) ignores the region.
}
public void testSEOLsymbols() {
@@ -579,4 +596,14 @@
assertTrue(pattern.matcher("14pt").matches());
}
+ public void testUnicodeCharacterClasses() throws Exception {
+ // http://code.google.com/p/android/issues/detail?id=21176
+ // We use the Unicode TR-18 definitions: http://www.unicode.org/reports/tr18/#Compatibility_Properties
+ assertTrue("\u0666".matches("\\d")); // ARABIC-INDIC DIGIT SIX
+ assertFalse("\u0666".matches("\\D")); // ARABIC-INDIC DIGIT SIX
+ assertTrue("\u1680".matches("\\s")); // OGHAM SPACE MARK
+ assertFalse("\u1680".matches("\\S")); // OGHAM SPACE MARK
+ assertTrue("\u00ea".matches("\\w")); // LATIN SMALL LETTER E WITH CIRCUMFLEX
+ assertFalse("\u00ea".matches("\\W")); // LATIN SMALL LETTER E WITH CIRCUMFLEX
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index 7919b35..d31825a 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -17,14 +17,489 @@
package libcore.javax.crypto;
import com.android.org.bouncycastle.asn1.x509.KeyUsage;
+import java.math.BigInteger;
import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Provider.Service;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
import java.security.cert.Certificate;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.IllegalBlockSizeException;
import junit.framework.TestCase;
import libcore.java.security.TestKeyStore;
public final class CipherTest extends TestCase {
+ private static boolean isUnsupported(String algorithm) {
+ if (algorithm.equals("PBEWITHMD5ANDRC2")) {
+ return true;
+ }
+ if (algorithm.equals("PBEWITHSHA1ANDRC2")) {
+ return true;
+ }
+ if (algorithm.equals("PBEWITHSHAAND40BITRC2-CBC")) {
+ return true;
+ }
+ if (algorithm.equals("PBEWITHSHAAND128BITRC2-CBC")) {
+ return true;
+ }
+ if (algorithm.equals("PBEWITHSHAANDTWOFISH-CBC")) {
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized static int getEncryptMode(String algorithm) throws Exception {
+ if (isWrap(algorithm)) {
+ return Cipher.WRAP_MODE;
+ }
+ return Cipher.ENCRYPT_MODE;
+ }
+
+ private synchronized static int getDecryptMode(String algorithm) throws Exception {
+ if (isWrap(algorithm)) {
+ return Cipher.UNWRAP_MODE;
+ }
+ return Cipher.DECRYPT_MODE;
+ }
+
+ private static String getBaseAlgoritm(String algorithm) {
+ if (algorithm.equals("AESWRAP")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHMD5AND128BITAES-CBC-OPENSSL")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHMD5AND192BITAES-CBC-OPENSSL")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHMD5AND256BITAES-CBC-OPENSSL")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHSHA256AND128BITAES-CBC-BC")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHSHA256AND192BITAES-CBC-BC")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHSHA256AND256BITAES-CBC-BC")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHSHAAND128BITAES-CBC-BC")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHSHAAND192BITAES-CBC-BC")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHSHAAND256BITAES-CBC-BC")) {
+ return "AES";
+ }
+ if (algorithm.equals("PBEWITHMD5ANDDES")) {
+ return "DES";
+ }
+ if (algorithm.equals("PBEWITHSHA1ANDDES")) {
+ return "DES";
+ }
+ if (algorithm.equals("DESEDEWRAP")) {
+ return "DESede";
+ }
+ if (algorithm.equals("PBEWITHSHAAND2-KEYTRIPLEDES-CBC")) {
+ return "DESede";
+ }
+ if (algorithm.equals("PBEWITHSHAAND3-KEYTRIPLEDES-CBC")) {
+ return "DESede";
+ }
+ if (algorithm.equals("RSA/ECB/NoPadding")) {
+ return "RSA";
+ }
+ if (algorithm.equals("RSA/ECB/PKCS1Padding")) {
+ return "RSA";
+ }
+ if (algorithm.equals("PBEWITHSHAAND40BITRC4")) {
+ return "ARC4";
+ }
+ if (algorithm.equals("PBEWITHSHAAND128BITRC4")) {
+ return "ARC4";
+ }
+ return algorithm;
+ }
+
+ private static boolean isAsymmetric(String algorithm) {
+ return getBaseAlgoritm(algorithm).equals("RSA");
+ }
+
+ private static boolean isWrap(String algorithm) {
+ return algorithm.endsWith("WRAP");
+ }
+
+ private static Map<String, Key> ENCRYPT_KEYS = new HashMap<String, Key>();
+ private synchronized static Key getEncryptKey(String algorithm) throws Exception {
+ Key key = ENCRYPT_KEYS.get(algorithm);
+ if (key != null) {
+ return key;
+ }
+ algorithm = getBaseAlgoritm(algorithm);
+ if (algorithm.equals("RSA")) {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+ key = kf.generatePrivate(keySpec);
+ } else {
+ KeyGenerator kg = KeyGenerator.getInstance(algorithm);
+ key = kg.generateKey();
+ }
+ ENCRYPT_KEYS.put(algorithm, key);
+ return key;
+ }
+
+ private static Map<String, Key> DECRYPT_KEYS = new HashMap<String, Key>();
+ private synchronized static Key getDecryptKey(String algorithm) throws Exception {
+ Key key = DECRYPT_KEYS.get(algorithm);
+ if (key != null) {
+ return key;
+ }
+ algorithm = getBaseAlgoritm(algorithm);
+ if (algorithm.equals("RSA")) {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ key = kf.generatePublic(keySpec);
+ } else {
+ assertFalse(algorithm, isAsymmetric(algorithm));
+ key = getEncryptKey(algorithm);
+ }
+ DECRYPT_KEYS.put(algorithm, key);
+ return key;
+ }
+
+ private static Map<String, Integer> EXPECTED_BLOCK_SIZE = new HashMap<String, Integer>();
+ static {
+ EXPECTED_BLOCK_SIZE.put("AES", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHA256AND128BITAES-CBC-BC", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHA256AND192BITAES-CBC-BC", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHA256AND256BITAES-CBC-BC", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHAAND128BITAES-CBC-BC", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHAAND192BITAES-CBC-BC", 16);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHAAND256BITAES-CBC-BC", 16);
+
+ EXPECTED_BLOCK_SIZE.put("AESWRAP", 0);
+
+ EXPECTED_BLOCK_SIZE.put("ARC4", 0);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHAAND40BITRC4", 0);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHAAND128BITRC4", 0);
+
+ EXPECTED_BLOCK_SIZE.put("BLOWFISH", 8);
+
+ EXPECTED_BLOCK_SIZE.put("DES", 8);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHMD5ANDDES", 8);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHA1ANDDES", 8);
+
+ EXPECTED_BLOCK_SIZE.put("DESEDE", 8);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", 8);
+ EXPECTED_BLOCK_SIZE.put("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", 8);
+
+ EXPECTED_BLOCK_SIZE.put("DESEDEWRAP", 0);
+
+ EXPECTED_BLOCK_SIZE.put("RSA", 255);
+ EXPECTED_BLOCK_SIZE.put("RSA/ECB/NoPadding", 0);
+ EXPECTED_BLOCK_SIZE.put("RSA/ECB/PKCS1Padding", 0);
+ }
+ private static int getExpectedBlockSize(String algorithm) {
+ Integer expected = EXPECTED_BLOCK_SIZE.get(algorithm);
+ assertNotNull(algorithm, expected);
+ return expected;
+ }
+
+ private static Map<String, Integer> EXPECTED_OUTPUT_SIZE = new HashMap<String, Integer>();
+ static {
+ EXPECTED_OUTPUT_SIZE.put("AES", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHA256AND128BITAES-CBC-BC", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHA256AND192BITAES-CBC-BC", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHA256AND256BITAES-CBC-BC", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHAAND128BITAES-CBC-BC", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHAAND192BITAES-CBC-BC", 16);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHAAND256BITAES-CBC-BC", 16);
+
+ EXPECTED_OUTPUT_SIZE.put("AESWRAP", -1);
+
+ EXPECTED_OUTPUT_SIZE.put("ARC4", 0);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHAAND40BITRC4", 0);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHAAND128BITRC4", 0);
+
+ EXPECTED_OUTPUT_SIZE.put("BLOWFISH", 8);
+
+ EXPECTED_OUTPUT_SIZE.put("DES", 8);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHMD5ANDDES", 8);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHA1ANDDES", 8);
+
+ EXPECTED_OUTPUT_SIZE.put("DESEDE", 8);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", 8);
+ EXPECTED_OUTPUT_SIZE.put("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", 8);
+
+ EXPECTED_OUTPUT_SIZE.put("DESEDEWRAP", -1);
+
+ EXPECTED_OUTPUT_SIZE.put("RSA", 256);
+ EXPECTED_OUTPUT_SIZE.put("RSA/ECB/NoPadding", 256);
+ EXPECTED_OUTPUT_SIZE.put("RSA/ECB/PKCS1Padding", 256);
+ }
+ private static int getExpectedOutputSize(String algorithm) {
+ Integer expected = EXPECTED_OUTPUT_SIZE.get(algorithm);
+ assertNotNull(algorithm, expected);
+ return expected;
+ }
+
+ private static byte[] ORIGINAL_PLAIN_TEXT = new byte[] { 0x0a, 0x0b, 0x0c };
+ private static byte[] PKCS1_BLOCK_TYPE_00_PADDED_PLAIN_TEXT = new byte[] {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0x0b, 0x0c
+ };
+ private static byte[] PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT = new byte[] {
+ (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c
+ };
+ private static byte[] PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT = new byte[] {
+ (byte) 0x00, (byte) 0x02, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c
+ };
+
+ private static byte[] getExpectedPlainText(String algorithm) {
+ if (algorithm.equals("RSA/ECB/NoPadding")) {
+ return PKCS1_BLOCK_TYPE_00_PADDED_PLAIN_TEXT;
+ }
+ return ORIGINAL_PLAIN_TEXT;
+ }
+
+ public void test_getInstance() throws Exception {
+ Provider[] providers = Security.getProviders();
+ for (Provider provider : providers) {
+ Set<Provider.Service> services = provider.getServices();
+ for (Provider.Service service : services) {
+ String type = service.getType();
+ if (!type.equals("Cipher")) {
+ continue;
+ }
+ String algorithm = service.getAlgorithm();
+ try {
+ // Cipher.getInstance(String)
+ Cipher c1 = Cipher.getInstance(algorithm);
+ assertEquals(algorithm, c1.getAlgorithm());
+ test_Cipher(c1);
+
+ // Cipher.getInstance(String, Provider)
+ Cipher c2 = Cipher.getInstance(algorithm, provider);
+ assertEquals(algorithm, c2.getAlgorithm());
+ assertEquals(provider, c2.getProvider());
+ test_Cipher(c2);
+
+ // KeyGenerator.getInstance(String, String)
+ Cipher c3 = Cipher.getInstance(algorithm, provider.getName());
+ assertEquals(algorithm, c3.getAlgorithm());
+ assertEquals(provider, c3.getProvider());
+ test_Cipher(c3);
+ } catch (Throwable e) {
+ throw new Exception("Problem testing Cipher." + algorithm, e);
+ }
+ }
+ }
+ }
+
+ private void test_Cipher(Cipher c) throws Exception {
+ // TODO: test all supported modes and padding for a given algorithm
+ String algorithm = c.getAlgorithm();
+ if (isUnsupported(algorithm)) {
+ return;
+ }
+
+ try {
+ c.getOutputSize(0);
+ } catch (IllegalStateException expected) {
+ }
+
+ // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs JCERSAPrivateKey)
+ Key encryptKey = getEncryptKey(algorithm);
+ c.init(getEncryptMode(algorithm), encryptKey);
+
+ assertEquals(getExpectedBlockSize(algorithm), c.getBlockSize());
+
+ assertEquals(getExpectedOutputSize(algorithm), c.getOutputSize(0));
+
+ // TODO: test Cipher.getIV()
+
+ // TODO: test Cipher.getParameters()
+
+ assertNull(c.getExemptionMechanism());
+
+ if (isWrap(algorithm)) {
+ byte[] cipherText = c.wrap(encryptKey);
+ c.init(getDecryptMode(algorithm), getDecryptKey(algorithm));
+ int keyType = (isAsymmetric(algorithm)) ? Cipher.PRIVATE_KEY : Cipher.SECRET_KEY;
+ Key decryptedKey = c.unwrap(cipherText, encryptKey.getAlgorithm(), keyType);
+ assertEquals("encryptKey.getAlgorithm()=" + encryptKey.getAlgorithm()
+ + " decryptedKey.getAlgorithm()=" + decryptedKey.getAlgorithm()
+ + " encryptKey.getEncoded()=" + Arrays.toString(encryptKey.getEncoded())
+ + " decryptedKey.getEncoded()=" + Arrays.toString(decryptedKey.getEncoded()),
+ encryptKey, decryptedKey);
+ } else {
+ byte[] cipherText = c.doFinal(ORIGINAL_PLAIN_TEXT);
+ c.init(getDecryptMode(algorithm), getDecryptKey(algorithm));
+ byte[] decryptedPlainText = c.doFinal(cipherText);
+ assertEquals(Arrays.toString(getExpectedPlainText(algorithm)),
+ Arrays.toString(decryptedPlainText));
+ }
+ }
+
+ public void testInputPKCS1Padding() throws Exception {
+ testInputPKCS1Padding(PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA"));
+ try {
+ testInputPKCS1Padding(PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA"));
+ fail();
+ } catch (BadPaddingException expected) {
+ }
+ try {
+ testInputPKCS1Padding(PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA"));
+ fail();
+ } catch (BadPaddingException expected) {
+ }
+ testInputPKCS1Padding(PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA"));
+ }
+
+ private void testInputPKCS1Padding(byte[] prePaddedPlainText, Key encryptKey, Key decryptKey) throws Exception {
+ Cipher encryptCipher = Cipher.getInstance("RSA/ECB/NoPadding");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey);
+ byte[] cipherText = encryptCipher.doFinal(prePaddedPlainText);
+ Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey);
+ byte[] plainText = decryptCipher.doFinal(cipherText);
+ assertEquals(Arrays.toString(ORIGINAL_PLAIN_TEXT),
+ Arrays.toString(plainText));
+ }
+
+ public void testOutputPKCS1Padding() throws Exception {
+ testOutputPKCS1Padding((byte) 1, getEncryptKey("RSA"), getDecryptKey("RSA"));
+ testOutputPKCS1Padding((byte) 2, getDecryptKey("RSA"), getEncryptKey("RSA"));
+ }
+
+ private void testOutputPKCS1Padding(byte expectedBlockType, Key encryptKey, Key decryptKey) throws Exception {
+ Cipher encryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey);
+ byte[] cipherText = encryptCipher.doFinal(ORIGINAL_PLAIN_TEXT);
+ Cipher decryptCipher = Cipher.getInstance("RSA/ECB/NoPadding");
+ decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey);
+ byte[] plainText = decryptCipher.doFinal(cipherText);
+ assertPadding(expectedBlockType, ORIGINAL_PLAIN_TEXT, plainText);
+ }
+
+ private void assertPadding(byte expectedBlockType, byte[] expectedData, byte[] actualDataWithPadding) {
+ assertNotNull(actualDataWithPadding);
+ assertEquals(getExpectedOutputSize("RSA"), actualDataWithPadding.length);
+ assertEquals(0, actualDataWithPadding[0]);
+ byte actualBlockType = actualDataWithPadding[1];
+ assertEquals(expectedBlockType, actualBlockType);
+ int actualDataOffset = actualDataWithPadding.length - expectedData.length;
+ if (actualBlockType == 1) {
+ for (int i = 2; i < actualDataOffset - 1; i++) {
+ assertEquals((byte) 0xFF, actualDataWithPadding[i]);
+ }
+ }
+ assertEquals(0x00, actualDataWithPadding[actualDataOffset-1]);
+ byte[] actualData = new byte[expectedData.length];
+ System.arraycopy(actualDataWithPadding, actualDataOffset, actualData, 0, actualData.length);
+ assertEquals(Arrays.toString(expectedData), Arrays.toString(actualData));
+ }
+
public void testCipherInitWithCertificate () throws Exception {
// no key usage specified, everything is fine
assertCipherInitWithKeyUsage(0, true, true, true, true);
@@ -108,4 +583,774 @@
.build()
.getPrivateKey("RSA", "RSA").getCertificate();
}
+
+ /*
+ * Test vectors generated with this private key:
+ *
+ * -----BEGIN RSA PRIVATE KEY-----
+ * MIIEpAIBAAKCAQEA4Ec+irjyKE/rnnQv+XSPoRjtmGM8kvUq63ouvg075gMpvnZq
+ * 0Q62pRXQ0s/ZvqeTDwwwZTeJn3lYzT6FsB+IGFJNMSWEqUslHjYltUFB7b/uGYgI
+ * 4buX/Hy0m56qr2jpyY19DtxTu8D6ADQ1bWMF+7zDxwAUBThqu8hzyw8+90JfPTPf
+ * ezFa4DbSoLZq/UdQOxab8247UWJRW3Ff2oPeryxYrrmr+zCXw8yd2dvl7ylsF2E5
+ * Ao6KZx5jBW1F9AGI0sQTNJCEXeUsJTTpxrJHjAe9rpKII7YtBmx3cPn2Pz26JH9T
+ * CER0e+eqqF2FO4vSRKzsPePImrRkU6tNJMOsaQIDAQABAoIBADd4R3al8XaY9ayW
+ * DfuDobZ1ZOZIvQWXz4q4CHGG8macJ6nsvdSA8Bl6gNBzCebGqW+SUzHlf4tKxvTU
+ * XtpFojJpwJ/EKMB6Tm7fc4oV3sl/q9Lyu0ehTyDqcvz+TDbgGtp3vRN82NTaELsW
+ * LpSkZilx8XX5hfoYjwVsuX7igW9Dq503R2Ekhs2owWGWwwgYqZXshdOEZ3kSZ7O/
+ * IfJzcQppJYYldoQcW2cSwS1L0govMpmtt8E12l6VFavadufK8qO+gFUdBzt4vxFi
+ * xIrSt/R0OgI47k0lL31efmUzzK5kzLOTYAdaL9HgNOw65c6cQIzL8OJeQRQCFoez
+ * 3UdUroECgYEA9UGIS8Nzeyki1BGe9F4t7izUy7dfRVBaFXqlAJ+Zxzot8HJKxGAk
+ * MGMy6omBd2NFRl3G3x4KbxQK/ztzluaomUrF2qloc0cv43dJ0U6z4HXmKdvrNYMz
+ * im82SdCiZUp6Qv2atr+krE1IHTkLsimwZL3DEcwb4bYxidp8QM3s8rECgYEA6hp0
+ * LduIHO23KIyH442GjdekCdFaQ/RF1Td6C1cx3b/KLa8oqOE81cCvzsM0fXSjniNa
+ * PNljPydN4rlPkt9DgzkR2enxz1jyfeLgj/RZZMcg0+whOdx8r8kSlTzeyy81Wi4s
+ * NaUPrXVMs7IxZkJLo7bjESoriYw4xcFe2yOGkzkCgYBRgo8exv2ZYCmQG68dfjN7
+ * pfCvJ+mE6tiVrOYr199O5FoiQInyzBUa880XP84EdLywTzhqLNzA4ANrokGfVFeS
+ * YtRxAL6TGYSj76Bb7PFBV03AebOpXEqD5sQ/MhTW3zLVEt4ZgIXlMeYWuD/X3Z0f
+ * TiYHwzM9B8VdEH0dOJNYcQKBgQDbT7UPUN6O21P/NMgJMYigUShn2izKBIl3WeWH
+ * wkQBDa+GZNWegIPRbBZHiTAfZ6nweAYNg0oq29NnV1toqKhCwrAqibPzH8zsiiL+
+ * OVeVxcbHQitOXXSh6ajzDndZufwtY5wfFWc+hOk6XvFQb0MVODw41Fy9GxQEj0ch
+ * 3IIyYQKBgQDYEUWTr0FfthLb8ZI3ENVNB0hiBadqO0MZSWjA3/HxHvD2GkozfV/T
+ * dBu8lkDkR7i2tsR8OsEgQ1fTsMVbqShr2nP2KSlvX6kUbYl2NX08dR51FIaWpAt0
+ * aFyCzjCQLWOdck/yTV4ulAfuNO3tLjtN9lqpvP623yjQe6aQPxZXaA==
+ * -----END RSA PRIVATE KEY-----
+ *
+ */
+
+ private static final BigInteger RSA_2048_modulus = new BigInteger(new byte[] {
+ (byte) 0x00, (byte) 0xe0, (byte) 0x47, (byte) 0x3e, (byte) 0x8a, (byte) 0xb8, (byte) 0xf2, (byte) 0x28,
+ (byte) 0x4f, (byte) 0xeb, (byte) 0x9e, (byte) 0x74, (byte) 0x2f, (byte) 0xf9, (byte) 0x74, (byte) 0x8f,
+ (byte) 0xa1, (byte) 0x18, (byte) 0xed, (byte) 0x98, (byte) 0x63, (byte) 0x3c, (byte) 0x92, (byte) 0xf5,
+ (byte) 0x2a, (byte) 0xeb, (byte) 0x7a, (byte) 0x2e, (byte) 0xbe, (byte) 0x0d, (byte) 0x3b, (byte) 0xe6,
+ (byte) 0x03, (byte) 0x29, (byte) 0xbe, (byte) 0x76, (byte) 0x6a, (byte) 0xd1, (byte) 0x0e, (byte) 0xb6,
+ (byte) 0xa5, (byte) 0x15, (byte) 0xd0, (byte) 0xd2, (byte) 0xcf, (byte) 0xd9, (byte) 0xbe, (byte) 0xa7,
+ (byte) 0x93, (byte) 0x0f, (byte) 0x0c, (byte) 0x30, (byte) 0x65, (byte) 0x37, (byte) 0x89, (byte) 0x9f,
+ (byte) 0x79, (byte) 0x58, (byte) 0xcd, (byte) 0x3e, (byte) 0x85, (byte) 0xb0, (byte) 0x1f, (byte) 0x88,
+ (byte) 0x18, (byte) 0x52, (byte) 0x4d, (byte) 0x31, (byte) 0x25, (byte) 0x84, (byte) 0xa9, (byte) 0x4b,
+ (byte) 0x25, (byte) 0x1e, (byte) 0x36, (byte) 0x25, (byte) 0xb5, (byte) 0x41, (byte) 0x41, (byte) 0xed,
+ (byte) 0xbf, (byte) 0xee, (byte) 0x19, (byte) 0x88, (byte) 0x08, (byte) 0xe1, (byte) 0xbb, (byte) 0x97,
+ (byte) 0xfc, (byte) 0x7c, (byte) 0xb4, (byte) 0x9b, (byte) 0x9e, (byte) 0xaa, (byte) 0xaf, (byte) 0x68,
+ (byte) 0xe9, (byte) 0xc9, (byte) 0x8d, (byte) 0x7d, (byte) 0x0e, (byte) 0xdc, (byte) 0x53, (byte) 0xbb,
+ (byte) 0xc0, (byte) 0xfa, (byte) 0x00, (byte) 0x34, (byte) 0x35, (byte) 0x6d, (byte) 0x63, (byte) 0x05,
+ (byte) 0xfb, (byte) 0xbc, (byte) 0xc3, (byte) 0xc7, (byte) 0x00, (byte) 0x14, (byte) 0x05, (byte) 0x38,
+ (byte) 0x6a, (byte) 0xbb, (byte) 0xc8, (byte) 0x73, (byte) 0xcb, (byte) 0x0f, (byte) 0x3e, (byte) 0xf7,
+ (byte) 0x42, (byte) 0x5f, (byte) 0x3d, (byte) 0x33, (byte) 0xdf, (byte) 0x7b, (byte) 0x31, (byte) 0x5a,
+ (byte) 0xe0, (byte) 0x36, (byte) 0xd2, (byte) 0xa0, (byte) 0xb6, (byte) 0x6a, (byte) 0xfd, (byte) 0x47,
+ (byte) 0x50, (byte) 0x3b, (byte) 0x16, (byte) 0x9b, (byte) 0xf3, (byte) 0x6e, (byte) 0x3b, (byte) 0x51,
+ (byte) 0x62, (byte) 0x51, (byte) 0x5b, (byte) 0x71, (byte) 0x5f, (byte) 0xda, (byte) 0x83, (byte) 0xde,
+ (byte) 0xaf, (byte) 0x2c, (byte) 0x58, (byte) 0xae, (byte) 0xb9, (byte) 0xab, (byte) 0xfb, (byte) 0x30,
+ (byte) 0x97, (byte) 0xc3, (byte) 0xcc, (byte) 0x9d, (byte) 0xd9, (byte) 0xdb, (byte) 0xe5, (byte) 0xef,
+ (byte) 0x29, (byte) 0x6c, (byte) 0x17, (byte) 0x61, (byte) 0x39, (byte) 0x02, (byte) 0x8e, (byte) 0x8a,
+ (byte) 0x67, (byte) 0x1e, (byte) 0x63, (byte) 0x05, (byte) 0x6d, (byte) 0x45, (byte) 0xf4, (byte) 0x01,
+ (byte) 0x88, (byte) 0xd2, (byte) 0xc4, (byte) 0x13, (byte) 0x34, (byte) 0x90, (byte) 0x84, (byte) 0x5d,
+ (byte) 0xe5, (byte) 0x2c, (byte) 0x25, (byte) 0x34, (byte) 0xe9, (byte) 0xc6, (byte) 0xb2, (byte) 0x47,
+ (byte) 0x8c, (byte) 0x07, (byte) 0xbd, (byte) 0xae, (byte) 0x92, (byte) 0x88, (byte) 0x23, (byte) 0xb6,
+ (byte) 0x2d, (byte) 0x06, (byte) 0x6c, (byte) 0x77, (byte) 0x70, (byte) 0xf9, (byte) 0xf6, (byte) 0x3f,
+ (byte) 0x3d, (byte) 0xba, (byte) 0x24, (byte) 0x7f, (byte) 0x53, (byte) 0x08, (byte) 0x44, (byte) 0x74,
+ (byte) 0x7b, (byte) 0xe7, (byte) 0xaa, (byte) 0xa8, (byte) 0x5d, (byte) 0x85, (byte) 0x3b, (byte) 0x8b,
+ (byte) 0xd2, (byte) 0x44, (byte) 0xac, (byte) 0xec, (byte) 0x3d, (byte) 0xe3, (byte) 0xc8, (byte) 0x9a,
+ (byte) 0xb4, (byte) 0x64, (byte) 0x53, (byte) 0xab, (byte) 0x4d, (byte) 0x24, (byte) 0xc3, (byte) 0xac,
+ (byte) 0x69,
+ });
+
+ private static final BigInteger RSA_2048_privateExponent = new BigInteger(new byte[] {
+ (byte) 0x37, (byte) 0x78, (byte) 0x47, (byte) 0x76, (byte) 0xa5, (byte) 0xf1, (byte) 0x76, (byte) 0x98,
+ (byte) 0xf5, (byte) 0xac, (byte) 0x96, (byte) 0x0d, (byte) 0xfb, (byte) 0x83, (byte) 0xa1, (byte) 0xb6,
+ (byte) 0x75, (byte) 0x64, (byte) 0xe6, (byte) 0x48, (byte) 0xbd, (byte) 0x05, (byte) 0x97, (byte) 0xcf,
+ (byte) 0x8a, (byte) 0xb8, (byte) 0x08, (byte) 0x71, (byte) 0x86, (byte) 0xf2, (byte) 0x66, (byte) 0x9c,
+ (byte) 0x27, (byte) 0xa9, (byte) 0xec, (byte) 0xbd, (byte) 0xd4, (byte) 0x80, (byte) 0xf0, (byte) 0x19,
+ (byte) 0x7a, (byte) 0x80, (byte) 0xd0, (byte) 0x73, (byte) 0x09, (byte) 0xe6, (byte) 0xc6, (byte) 0xa9,
+ (byte) 0x6f, (byte) 0x92, (byte) 0x53, (byte) 0x31, (byte) 0xe5, (byte) 0x7f, (byte) 0x8b, (byte) 0x4a,
+ (byte) 0xc6, (byte) 0xf4, (byte) 0xd4, (byte) 0x5e, (byte) 0xda, (byte) 0x45, (byte) 0xa2, (byte) 0x32,
+ (byte) 0x69, (byte) 0xc0, (byte) 0x9f, (byte) 0xc4, (byte) 0x28, (byte) 0xc0, (byte) 0x7a, (byte) 0x4e,
+ (byte) 0x6e, (byte) 0xdf, (byte) 0x73, (byte) 0x8a, (byte) 0x15, (byte) 0xde, (byte) 0xc9, (byte) 0x7f,
+ (byte) 0xab, (byte) 0xd2, (byte) 0xf2, (byte) 0xbb, (byte) 0x47, (byte) 0xa1, (byte) 0x4f, (byte) 0x20,
+ (byte) 0xea, (byte) 0x72, (byte) 0xfc, (byte) 0xfe, (byte) 0x4c, (byte) 0x36, (byte) 0xe0, (byte) 0x1a,
+ (byte) 0xda, (byte) 0x77, (byte) 0xbd, (byte) 0x13, (byte) 0x7c, (byte) 0xd8, (byte) 0xd4, (byte) 0xda,
+ (byte) 0x10, (byte) 0xbb, (byte) 0x16, (byte) 0x2e, (byte) 0x94, (byte) 0xa4, (byte) 0x66, (byte) 0x29,
+ (byte) 0x71, (byte) 0xf1, (byte) 0x75, (byte) 0xf9, (byte) 0x85, (byte) 0xfa, (byte) 0x18, (byte) 0x8f,
+ (byte) 0x05, (byte) 0x6c, (byte) 0xb9, (byte) 0x7e, (byte) 0xe2, (byte) 0x81, (byte) 0x6f, (byte) 0x43,
+ (byte) 0xab, (byte) 0x9d, (byte) 0x37, (byte) 0x47, (byte) 0x61, (byte) 0x24, (byte) 0x86, (byte) 0xcd,
+ (byte) 0xa8, (byte) 0xc1, (byte) 0x61, (byte) 0x96, (byte) 0xc3, (byte) 0x08, (byte) 0x18, (byte) 0xa9,
+ (byte) 0x95, (byte) 0xec, (byte) 0x85, (byte) 0xd3, (byte) 0x84, (byte) 0x67, (byte) 0x79, (byte) 0x12,
+ (byte) 0x67, (byte) 0xb3, (byte) 0xbf, (byte) 0x21, (byte) 0xf2, (byte) 0x73, (byte) 0x71, (byte) 0x0a,
+ (byte) 0x69, (byte) 0x25, (byte) 0x86, (byte) 0x25, (byte) 0x76, (byte) 0x84, (byte) 0x1c, (byte) 0x5b,
+ (byte) 0x67, (byte) 0x12, (byte) 0xc1, (byte) 0x2d, (byte) 0x4b, (byte) 0xd2, (byte) 0x0a, (byte) 0x2f,
+ (byte) 0x32, (byte) 0x99, (byte) 0xad, (byte) 0xb7, (byte) 0xc1, (byte) 0x35, (byte) 0xda, (byte) 0x5e,
+ (byte) 0x95, (byte) 0x15, (byte) 0xab, (byte) 0xda, (byte) 0x76, (byte) 0xe7, (byte) 0xca, (byte) 0xf2,
+ (byte) 0xa3, (byte) 0xbe, (byte) 0x80, (byte) 0x55, (byte) 0x1d, (byte) 0x07, (byte) 0x3b, (byte) 0x78,
+ (byte) 0xbf, (byte) 0x11, (byte) 0x62, (byte) 0xc4, (byte) 0x8a, (byte) 0xd2, (byte) 0xb7, (byte) 0xf4,
+ (byte) 0x74, (byte) 0x3a, (byte) 0x02, (byte) 0x38, (byte) 0xee, (byte) 0x4d, (byte) 0x25, (byte) 0x2f,
+ (byte) 0x7d, (byte) 0x5e, (byte) 0x7e, (byte) 0x65, (byte) 0x33, (byte) 0xcc, (byte) 0xae, (byte) 0x64,
+ (byte) 0xcc, (byte) 0xb3, (byte) 0x93, (byte) 0x60, (byte) 0x07, (byte) 0x5a, (byte) 0x2f, (byte) 0xd1,
+ (byte) 0xe0, (byte) 0x34, (byte) 0xec, (byte) 0x3a, (byte) 0xe5, (byte) 0xce, (byte) 0x9c, (byte) 0x40,
+ (byte) 0x8c, (byte) 0xcb, (byte) 0xf0, (byte) 0xe2, (byte) 0x5e, (byte) 0x41, (byte) 0x14, (byte) 0x02,
+ (byte) 0x16, (byte) 0x87, (byte) 0xb3, (byte) 0xdd, (byte) 0x47, (byte) 0x54, (byte) 0xae, (byte) 0x81,
+ });
+
+ private static final BigInteger RSA_2048_publicExponent = new BigInteger(new byte[] {
+ (byte) 0x01, (byte) 0x00, (byte) 0x01,
+ });
+
+ private static final BigInteger RSA_2048_primeP = new BigInteger(new byte[] {
+ (byte) 0x00, (byte) 0xf5, (byte) 0x41, (byte) 0x88, (byte) 0x4b, (byte) 0xc3, (byte) 0x73, (byte) 0x7b,
+ (byte) 0x29, (byte) 0x22, (byte) 0xd4, (byte) 0x11, (byte) 0x9e, (byte) 0xf4, (byte) 0x5e, (byte) 0x2d,
+ (byte) 0xee, (byte) 0x2c, (byte) 0xd4, (byte) 0xcb, (byte) 0xb7, (byte) 0x5f, (byte) 0x45, (byte) 0x50,
+ (byte) 0x5a, (byte) 0x15, (byte) 0x7a, (byte) 0xa5, (byte) 0x00, (byte) 0x9f, (byte) 0x99, (byte) 0xc7,
+ (byte) 0x3a, (byte) 0x2d, (byte) 0xf0, (byte) 0x72, (byte) 0x4a, (byte) 0xc4, (byte) 0x60, (byte) 0x24,
+ (byte) 0x30, (byte) 0x63, (byte) 0x32, (byte) 0xea, (byte) 0x89, (byte) 0x81, (byte) 0x77, (byte) 0x63,
+ (byte) 0x45, (byte) 0x46, (byte) 0x5d, (byte) 0xc6, (byte) 0xdf, (byte) 0x1e, (byte) 0x0a, (byte) 0x6f,
+ (byte) 0x14, (byte) 0x0a, (byte) 0xff, (byte) 0x3b, (byte) 0x73, (byte) 0x96, (byte) 0xe6, (byte) 0xa8,
+ (byte) 0x99, (byte) 0x4a, (byte) 0xc5, (byte) 0xda, (byte) 0xa9, (byte) 0x68, (byte) 0x73, (byte) 0x47,
+ (byte) 0x2f, (byte) 0xe3, (byte) 0x77, (byte) 0x49, (byte) 0xd1, (byte) 0x4e, (byte) 0xb3, (byte) 0xe0,
+ (byte) 0x75, (byte) 0xe6, (byte) 0x29, (byte) 0xdb, (byte) 0xeb, (byte) 0x35, (byte) 0x83, (byte) 0x33,
+ (byte) 0x8a, (byte) 0x6f, (byte) 0x36, (byte) 0x49, (byte) 0xd0, (byte) 0xa2, (byte) 0x65, (byte) 0x4a,
+ (byte) 0x7a, (byte) 0x42, (byte) 0xfd, (byte) 0x9a, (byte) 0xb6, (byte) 0xbf, (byte) 0xa4, (byte) 0xac,
+ (byte) 0x4d, (byte) 0x48, (byte) 0x1d, (byte) 0x39, (byte) 0x0b, (byte) 0xb2, (byte) 0x29, (byte) 0xb0,
+ (byte) 0x64, (byte) 0xbd, (byte) 0xc3, (byte) 0x11, (byte) 0xcc, (byte) 0x1b, (byte) 0xe1, (byte) 0xb6,
+ (byte) 0x31, (byte) 0x89, (byte) 0xda, (byte) 0x7c, (byte) 0x40, (byte) 0xcd, (byte) 0xec, (byte) 0xf2,
+ (byte) 0xb1,
+ });
+
+ private static final BigInteger RSA_2048_primeQ = new BigInteger(new byte[] {
+ (byte) 0x00, (byte) 0xea, (byte) 0x1a, (byte) 0x74, (byte) 0x2d, (byte) 0xdb, (byte) 0x88, (byte) 0x1c,
+ (byte) 0xed, (byte) 0xb7, (byte) 0x28, (byte) 0x8c, (byte) 0x87, (byte) 0xe3, (byte) 0x8d, (byte) 0x86,
+ (byte) 0x8d, (byte) 0xd7, (byte) 0xa4, (byte) 0x09, (byte) 0xd1, (byte) 0x5a, (byte) 0x43, (byte) 0xf4,
+ (byte) 0x45, (byte) 0xd5, (byte) 0x37, (byte) 0x7a, (byte) 0x0b, (byte) 0x57, (byte) 0x31, (byte) 0xdd,
+ (byte) 0xbf, (byte) 0xca, (byte) 0x2d, (byte) 0xaf, (byte) 0x28, (byte) 0xa8, (byte) 0xe1, (byte) 0x3c,
+ (byte) 0xd5, (byte) 0xc0, (byte) 0xaf, (byte) 0xce, (byte) 0xc3, (byte) 0x34, (byte) 0x7d, (byte) 0x74,
+ (byte) 0xa3, (byte) 0x9e, (byte) 0x23, (byte) 0x5a, (byte) 0x3c, (byte) 0xd9, (byte) 0x63, (byte) 0x3f,
+ (byte) 0x27, (byte) 0x4d, (byte) 0xe2, (byte) 0xb9, (byte) 0x4f, (byte) 0x92, (byte) 0xdf, (byte) 0x43,
+ (byte) 0x83, (byte) 0x39, (byte) 0x11, (byte) 0xd9, (byte) 0xe9, (byte) 0xf1, (byte) 0xcf, (byte) 0x58,
+ (byte) 0xf2, (byte) 0x7d, (byte) 0xe2, (byte) 0xe0, (byte) 0x8f, (byte) 0xf4, (byte) 0x59, (byte) 0x64,
+ (byte) 0xc7, (byte) 0x20, (byte) 0xd3, (byte) 0xec, (byte) 0x21, (byte) 0x39, (byte) 0xdc, (byte) 0x7c,
+ (byte) 0xaf, (byte) 0xc9, (byte) 0x12, (byte) 0x95, (byte) 0x3c, (byte) 0xde, (byte) 0xcb, (byte) 0x2f,
+ (byte) 0x35, (byte) 0x5a, (byte) 0x2e, (byte) 0x2c, (byte) 0x35, (byte) 0xa5, (byte) 0x0f, (byte) 0xad,
+ (byte) 0x75, (byte) 0x4c, (byte) 0xb3, (byte) 0xb2, (byte) 0x31, (byte) 0x66, (byte) 0x42, (byte) 0x4b,
+ (byte) 0xa3, (byte) 0xb6, (byte) 0xe3, (byte) 0x11, (byte) 0x2a, (byte) 0x2b, (byte) 0x89, (byte) 0x8c,
+ (byte) 0x38, (byte) 0xc5, (byte) 0xc1, (byte) 0x5e, (byte) 0xdb, (byte) 0x23, (byte) 0x86, (byte) 0x93,
+ (byte) 0x39,
+ });
+
+ /**
+ * Test data is PKCS#1 padded "Android.\n" which can be generated by:
+ * echo "Android." | openssl rsautl -inkey rsa.key -sign | openssl rsautl -inkey rsa.key -raw -verify | recode ../x1
+ */
+ private static final byte[] RSA_2048_Vector1 = new byte[] {
+ (byte) 0x00, (byte) 0x01, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0x00, (byte) 0x41, (byte) 0x6E, (byte) 0x64, (byte) 0x72, (byte) 0x6F,
+ (byte) 0x69, (byte) 0x64, (byte) 0x2E, (byte) 0x0A,
+ };
+
+ /**
+ * This vector is simply "Android.\n" which is too short.
+ */
+ private static final byte[] TooShort_Vector = new byte[] {
+ (byte) 0x41, (byte) 0x6E, (byte) 0x64, (byte) 0x72, (byte) 0x6F, (byte) 0x69,
+ (byte) 0x64, (byte) 0x2E, (byte) 0x0A,
+ };
+
+ /**
+ * This vector is simply "Android.\n" padded with zeros.
+ */
+ private static final byte[] TooShort_Vector_Zero_Padded = new byte[] {
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f,
+ (byte) 0x69, (byte) 0x64, (byte) 0x2e, (byte) 0x0a,
+ };
+
+ /**
+ * openssl rsautl -raw -sign -inkey rsa.key | recode ../x1 | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] RSA_Vector1_Encrypt_Private = new byte[] {
+ (byte) 0x35, (byte) 0x43, (byte) 0x38, (byte) 0x44, (byte) 0xAD, (byte) 0x3F,
+ (byte) 0x97, (byte) 0x02, (byte) 0xFB, (byte) 0x59, (byte) 0x1F, (byte) 0x4A,
+ (byte) 0x2B, (byte) 0xB9, (byte) 0x06, (byte) 0xEC, (byte) 0x66, (byte) 0xE6,
+ (byte) 0xD2, (byte) 0xC5, (byte) 0x8B, (byte) 0x7B, (byte) 0xE3, (byte) 0x18,
+ (byte) 0xBF, (byte) 0x07, (byte) 0xD6, (byte) 0x01, (byte) 0xF9, (byte) 0xD9,
+ (byte) 0x89, (byte) 0xC4, (byte) 0xDB, (byte) 0x00, (byte) 0x68, (byte) 0xFF,
+ (byte) 0x9B, (byte) 0x43, (byte) 0x90, (byte) 0xF2, (byte) 0xDB, (byte) 0x83,
+ (byte) 0xF4, (byte) 0x7E, (byte) 0xC6, (byte) 0x81, (byte) 0x01, (byte) 0x3A,
+ (byte) 0x0B, (byte) 0xE5, (byte) 0xED, (byte) 0x08, (byte) 0x73, (byte) 0x3E,
+ (byte) 0xE1, (byte) 0x3F, (byte) 0xDF, (byte) 0x1F, (byte) 0x07, (byte) 0x6D,
+ (byte) 0x22, (byte) 0x8D, (byte) 0xCC, (byte) 0x4E, (byte) 0xE3, (byte) 0x9A,
+ (byte) 0xBC, (byte) 0xCC, (byte) 0x8F, (byte) 0x9E, (byte) 0x9B, (byte) 0x02,
+ (byte) 0x48, (byte) 0x00, (byte) 0xAC, (byte) 0x9F, (byte) 0xA4, (byte) 0x8F,
+ (byte) 0x87, (byte) 0xA1, (byte) 0xA8, (byte) 0xE6, (byte) 0x9D, (byte) 0xCD,
+ (byte) 0x8B, (byte) 0x05, (byte) 0xE9, (byte) 0xD2, (byte) 0x05, (byte) 0x8D,
+ (byte) 0xC9, (byte) 0x95, (byte) 0x16, (byte) 0xD0, (byte) 0xCD, (byte) 0x43,
+ (byte) 0x25, (byte) 0x8A, (byte) 0x11, (byte) 0x46, (byte) 0xD7, (byte) 0x74,
+ (byte) 0x4C, (byte) 0xCF, (byte) 0x58, (byte) 0xF9, (byte) 0xA1, (byte) 0x30,
+ (byte) 0x84, (byte) 0x52, (byte) 0xC9, (byte) 0x01, (byte) 0x5F, (byte) 0x24,
+ (byte) 0x4C, (byte) 0xB1, (byte) 0x9F, (byte) 0x7D, (byte) 0x12, (byte) 0x38,
+ (byte) 0x27, (byte) 0x0F, (byte) 0x5E, (byte) 0xFF, (byte) 0xE0, (byte) 0x55,
+ (byte) 0x8B, (byte) 0xA3, (byte) 0xAD, (byte) 0x60, (byte) 0x35, (byte) 0x83,
+ (byte) 0x58, (byte) 0xAF, (byte) 0x99, (byte) 0xDE, (byte) 0x3F, (byte) 0x5D,
+ (byte) 0x80, (byte) 0x80, (byte) 0xFF, (byte) 0x9B, (byte) 0xDE, (byte) 0x5C,
+ (byte) 0xAB, (byte) 0x97, (byte) 0x43, (byte) 0x64, (byte) 0xD9, (byte) 0x9F,
+ (byte) 0xFB, (byte) 0x67, (byte) 0x65, (byte) 0xA5, (byte) 0x99, (byte) 0xE7,
+ (byte) 0xE6, (byte) 0xEB, (byte) 0x05, (byte) 0x95, (byte) 0xFC, (byte) 0x46,
+ (byte) 0x28, (byte) 0x4B, (byte) 0xD8, (byte) 0x8C, (byte) 0xF5, (byte) 0x0A,
+ (byte) 0xEB, (byte) 0x1F, (byte) 0x30, (byte) 0xEA, (byte) 0xE7, (byte) 0x67,
+ (byte) 0x11, (byte) 0x25, (byte) 0xF0, (byte) 0x44, (byte) 0x75, (byte) 0x74,
+ (byte) 0x94, (byte) 0x06, (byte) 0x78, (byte) 0xD0, (byte) 0x21, (byte) 0xF4,
+ (byte) 0x3F, (byte) 0xC8, (byte) 0xC4, (byte) 0x4A, (byte) 0x57, (byte) 0xBE,
+ (byte) 0x02, (byte) 0x3C, (byte) 0x93, (byte) 0xF6, (byte) 0x95, (byte) 0xFB,
+ (byte) 0xD1, (byte) 0x77, (byte) 0x8B, (byte) 0x43, (byte) 0xF0, (byte) 0xB9,
+ (byte) 0x7D, (byte) 0xE0, (byte) 0x32, (byte) 0xE1, (byte) 0x72, (byte) 0xB5,
+ (byte) 0x62, (byte) 0x3F, (byte) 0x86, (byte) 0xC3, (byte) 0xD4, (byte) 0x5F,
+ (byte) 0x5E, (byte) 0x54, (byte) 0x1B, (byte) 0x5B, (byte) 0xE6, (byte) 0x74,
+ (byte) 0xA1, (byte) 0x0B, (byte) 0xE5, (byte) 0x18, (byte) 0xD2, (byte) 0x4F,
+ (byte) 0x93, (byte) 0xF3, (byte) 0x09, (byte) 0x58, (byte) 0xCE, (byte) 0xF0,
+ (byte) 0xA3, (byte) 0x61, (byte) 0xE4, (byte) 0x6E, (byte) 0x46, (byte) 0x45,
+ (byte) 0x89, (byte) 0x50, (byte) 0xBD, (byte) 0x03, (byte) 0x3F, (byte) 0x38,
+ (byte) 0xDA, (byte) 0x5D, (byte) 0xD0, (byte) 0x1B, (byte) 0x1F, (byte) 0xB1,
+ (byte) 0xEE, (byte) 0x89, (byte) 0x59, (byte) 0xC5,
+ };
+
+ private static final byte[] RSA_Vector1_ZeroPadded_Encrypted = new byte[] {
+ (byte) 0x60, (byte) 0x4a, (byte) 0x12, (byte) 0xa3, (byte) 0xa7, (byte) 0x4a,
+ (byte) 0xa4, (byte) 0xbf, (byte) 0x6c, (byte) 0x36, (byte) 0xad, (byte) 0x66,
+ (byte) 0xdf, (byte) 0xce, (byte) 0xf1, (byte) 0xe4, (byte) 0x0f, (byte) 0xd4,
+ (byte) 0x54, (byte) 0x5f, (byte) 0x03, (byte) 0x15, (byte) 0x4b, (byte) 0x9e,
+ (byte) 0xeb, (byte) 0xfe, (byte) 0x9e, (byte) 0x24, (byte) 0xce, (byte) 0x8e,
+ (byte) 0xc3, (byte) 0x36, (byte) 0xa5, (byte) 0x76, (byte) 0xf6, (byte) 0x54,
+ (byte) 0xb7, (byte) 0x84, (byte) 0x48, (byte) 0x2f, (byte) 0xd4, (byte) 0x45,
+ (byte) 0x74, (byte) 0x48, (byte) 0x5f, (byte) 0x08, (byte) 0x4e, (byte) 0x9c,
+ (byte) 0x89, (byte) 0xcc, (byte) 0x34, (byte) 0x40, (byte) 0xb1, (byte) 0x5f,
+ (byte) 0xa7, (byte) 0x0e, (byte) 0x11, (byte) 0x4b, (byte) 0xb5, (byte) 0x94,
+ (byte) 0xbe, (byte) 0x14, (byte) 0xaa, (byte) 0xaa, (byte) 0xe0, (byte) 0x38,
+ (byte) 0x1c, (byte) 0xce, (byte) 0x40, (byte) 0x61, (byte) 0xfc, (byte) 0x08,
+ (byte) 0xcb, (byte) 0x14, (byte) 0x2b, (byte) 0xa6, (byte) 0x54, (byte) 0xdf,
+ (byte) 0x05, (byte) 0x5c, (byte) 0x9b, (byte) 0x4f, (byte) 0x14, (byte) 0x93,
+ (byte) 0xb0, (byte) 0x70, (byte) 0xd9, (byte) 0x32, (byte) 0xdc, (byte) 0x24,
+ (byte) 0xe0, (byte) 0xae, (byte) 0x48, (byte) 0xfc, (byte) 0x53, (byte) 0xee,
+ (byte) 0x7c, (byte) 0x9f, (byte) 0x69, (byte) 0x34, (byte) 0xf4, (byte) 0x76,
+ (byte) 0xee, (byte) 0x67, (byte) 0xb2, (byte) 0xa7, (byte) 0x33, (byte) 0x1c,
+ (byte) 0x47, (byte) 0xff, (byte) 0x5c, (byte) 0xf0, (byte) 0xb8, (byte) 0x04,
+ (byte) 0x2c, (byte) 0xfd, (byte) 0xe2, (byte) 0xb1, (byte) 0x4a, (byte) 0x0a,
+ (byte) 0x69, (byte) 0x1c, (byte) 0x80, (byte) 0x2b, (byte) 0xb4, (byte) 0x50,
+ (byte) 0x65, (byte) 0x5c, (byte) 0x76, (byte) 0x78, (byte) 0x9a, (byte) 0x0c,
+ (byte) 0x05, (byte) 0x62, (byte) 0xf0, (byte) 0xc4, (byte) 0x1c, (byte) 0x38,
+ (byte) 0x15, (byte) 0xd0, (byte) 0xe2, (byte) 0x5a, (byte) 0x3d, (byte) 0xb6,
+ (byte) 0xe0, (byte) 0x88, (byte) 0x85, (byte) 0xd1, (byte) 0x4f, (byte) 0x7e,
+ (byte) 0xfc, (byte) 0x77, (byte) 0x0d, (byte) 0x2a, (byte) 0x45, (byte) 0xd5,
+ (byte) 0xf8, (byte) 0x3c, (byte) 0x7b, (byte) 0x2d, (byte) 0x1b, (byte) 0x82,
+ (byte) 0xfe, (byte) 0x58, (byte) 0x22, (byte) 0x47, (byte) 0x06, (byte) 0x58,
+ (byte) 0x8b, (byte) 0x4f, (byte) 0xfb, (byte) 0x9b, (byte) 0x1c, (byte) 0x70,
+ (byte) 0x36, (byte) 0x12, (byte) 0x04, (byte) 0x17, (byte) 0x47, (byte) 0x8a,
+ (byte) 0x0a, (byte) 0xec, (byte) 0x12, (byte) 0x3b, (byte) 0xf8, (byte) 0xd2,
+ (byte) 0xdc, (byte) 0x3c, (byte) 0xc8, (byte) 0x46, (byte) 0xc6, (byte) 0x51,
+ (byte) 0x06, (byte) 0x06, (byte) 0xcb, (byte) 0x84, (byte) 0x67, (byte) 0xb5,
+ (byte) 0x68, (byte) 0xd9, (byte) 0x9c, (byte) 0xd4, (byte) 0x16, (byte) 0x5c,
+ (byte) 0xb4, (byte) 0xe2, (byte) 0x55, (byte) 0xe6, (byte) 0x3a, (byte) 0x73,
+ (byte) 0x01, (byte) 0x1d, (byte) 0x6f, (byte) 0x30, (byte) 0x31, (byte) 0x59,
+ (byte) 0x8b, (byte) 0x2f, (byte) 0x4c, (byte) 0xe7, (byte) 0x86, (byte) 0x4c,
+ (byte) 0x39, (byte) 0x4e, (byte) 0x67, (byte) 0x3b, (byte) 0x22, (byte) 0x9b,
+ (byte) 0x85, (byte) 0x5a, (byte) 0xc3, (byte) 0x29, (byte) 0xaf, (byte) 0x8c,
+ (byte) 0x7c, (byte) 0x59, (byte) 0x4a, (byte) 0x24, (byte) 0xfa, (byte) 0xba,
+ (byte) 0x55, (byte) 0x40, (byte) 0x13, (byte) 0x64, (byte) 0xd8, (byte) 0xcb,
+ (byte) 0x4b, (byte) 0x98, (byte) 0x3f, (byte) 0xae, (byte) 0x20, (byte) 0xfd,
+ (byte) 0x8a, (byte) 0x50, (byte) 0x73, (byte) 0xe4,
+ };
+
+ public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+
+ final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually decrypting with private keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ byte[] encrypted = c.doFinal(RSA_2048_Vector1);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ encrypted = c.doFinal(RSA_2048_Vector1);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Private_UpdateThenEmptyDoFinal_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+
+ final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually decrypting with private keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ c.update(RSA_2048_Vector1);
+ byte[] encrypted = c.doFinal();
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ c.update(RSA_2048_Vector1);
+ encrypted = c.doFinal();
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Private_SingleByteUpdateThenEmptyDoFinal_Success()
+ throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+
+ final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually decrypting with private keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ int i;
+ for (i = 0; i < RSA_2048_Vector1.length / 2; i++) {
+ c.update(RSA_2048_Vector1, i, 1);
+ }
+ byte[] encrypted = c.doFinal(RSA_2048_Vector1, i, RSA_2048_Vector1.length - i);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ for (i = 0; i < RSA_2048_Vector1.length / 2; i++) {
+ c.update(RSA_2048_Vector1, i, 1);
+ }
+ encrypted = c.doFinal(RSA_2048_Vector1, i, RSA_2048_Vector1.length - i);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Private_OnlyDoFinalWithOffset_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+ final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually decrypting with private keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ byte[] encrypted = new byte[RSA_Vector1_Encrypt_Private.length];
+ final int encryptLen = c
+ .doFinal(RSA_2048_Vector1, 0, RSA_2048_Vector1.length, encrypted, 0);
+ assertEquals("Encrypted size should match expected", RSA_Vector1_Encrypt_Private.length,
+ encryptLen);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ final int decryptLen = c
+ .doFinal(RSA_2048_Vector1, 0, RSA_2048_Vector1.length, encrypted, 0);
+ assertEquals("Encrypted size should match expected", RSA_Vector1_Encrypt_Private.length,
+ decryptLen);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+
+ final PublicKey privKey = kf.generatePublic(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ byte[] encrypted = c.doFinal(RSA_Vector1_Encrypt_Private);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_2048_Vector1, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ encrypted = c.doFinal(RSA_Vector1_Encrypt_Private);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_2048_Vector1, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+
+ final PublicKey pubKey = kf.generatePublic(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, pubKey);
+ byte[] encrypted = new byte[RSA_2048_Vector1.length];
+ final int encryptLen = c.doFinal(RSA_Vector1_Encrypt_Private, 0,
+ RSA_Vector1_Encrypt_Private.length, encrypted, 0);
+ assertEquals("Encrypted size should match expected", RSA_2048_Vector1.length, encryptLen);
+ assertTrue("Encrypted should match expected", Arrays.equals(RSA_2048_Vector1, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, pubKey);
+ final int decryptLen = c.doFinal(RSA_Vector1_Encrypt_Private, 0,
+ RSA_Vector1_Encrypt_Private.length, encrypted, 0);
+ assertEquals("Encrypted size should match expected", RSA_2048_Vector1.length, decryptLen);
+ assertTrue("Encrypted should match expected", Arrays.equals(RSA_2048_Vector1, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+
+ final PublicKey privKey = kf.generatePublic(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ c.update(RSA_Vector1_Encrypt_Private);
+ byte[] encrypted = c.doFinal();
+ assertTrue("Encrypted should match expected", Arrays.equals(RSA_2048_Vector1, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ c.update(RSA_Vector1_Encrypt_Private);
+ encrypted = c.doFinal();
+ assertTrue("Encrypted should match expected", Arrays.equals(RSA_2048_Vector1, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Success()
+ throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+
+ final PublicKey privKey = kf.generatePublic(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ int i;
+ for (i = 0; i < RSA_Vector1_Encrypt_Private.length / 2; i++) {
+ c.update(RSA_Vector1_Encrypt_Private, i, 1);
+ }
+ byte[] encrypted = c.doFinal(RSA_Vector1_Encrypt_Private, i, RSA_2048_Vector1.length - i);
+ assertTrue("Encrypted should match expected", Arrays.equals(RSA_2048_Vector1, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ for (i = 0; i < RSA_Vector1_Encrypt_Private.length / 2; i++) {
+ c.update(RSA_Vector1_Encrypt_Private, i, 1);
+ }
+ encrypted = c.doFinal(RSA_Vector1_Encrypt_Private, i, RSA_2048_Vector1.length - i);
+ assertTrue("Encrypted should match expected", Arrays.equals(RSA_2048_Vector1, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Public_TooSmall_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+
+ final PublicKey privKey = kf.generatePublic(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ byte[] encrypted = c.doFinal(TooShort_Vector);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_ZeroPadded_Encrypted, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ encrypted = c.doFinal(TooShort_Vector);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(RSA_Vector1_ZeroPadded_Encrypted, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Private_TooSmall_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+
+ final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ byte[] encrypted = c.doFinal(RSA_Vector1_ZeroPadded_Encrypted);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(TooShort_Vector_Zero_Padded, encrypted));
+
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ encrypted = c.doFinal(RSA_Vector1_ZeroPadded_Encrypted);
+ assertTrue("Encrypted should match expected",
+ Arrays.equals(TooShort_Vector_Zero_Padded, encrypted));
+ }
+
+ public void testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failure()
+ throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+
+ final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+ c.update(RSA_Vector1_ZeroPadded_Encrypted);
+
+ try {
+ c.doFinal(RSA_Vector1_ZeroPadded_Encrypted);
+ fail("Should have error when block size is too big.");
+ } catch (IllegalBlockSizeException success) {
+ }
+ }
+
+ public void testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failure()
+ throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+
+ final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+
+ byte[] output = new byte[RSA_2048_Vector1.length];
+ c.update(RSA_Vector1_ZeroPadded_Encrypted, 0, RSA_Vector1_ZeroPadded_Encrypted.length,
+ output);
+
+ try {
+ c.doFinal(RSA_Vector1_ZeroPadded_Encrypted);
+ fail("Should have error when block size is too big.");
+ } catch (IllegalBlockSizeException success) {
+ }
+ }
+
+ public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+ RSA_2048_privateExponent);
+
+ final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+
+ /*
+ * You're actually encrypting with public keys, but there is no
+ * distinction made here. It's all keyed off of what kind of key you're
+ * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+ */
+ c.init(Cipher.ENCRYPT_MODE, privKey);
+
+ byte[] tooBig_Vector = new byte[RSA_Vector1_ZeroPadded_Encrypted.length * 2];
+ System.arraycopy(RSA_Vector1_ZeroPadded_Encrypted, 0, tooBig_Vector, 0,
+ RSA_Vector1_ZeroPadded_Encrypted.length);
+ System.arraycopy(RSA_Vector1_ZeroPadded_Encrypted, 0, tooBig_Vector,
+ RSA_Vector1_ZeroPadded_Encrypted.length, RSA_Vector1_ZeroPadded_Encrypted.length);
+
+ try {
+ c.doFinal(tooBig_Vector);
+ fail("Should have error when block size is too big.");
+ } catch (IllegalBlockSizeException success) {
+ }
+ }
+
+ public void testRSA_ECB_NoPadding_GetBlockSize_Success() throws Exception {
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+ assertEquals("RSA is not a block cipher and should return block size of 0",
+ 0, c.getBlockSize());
+
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ final PublicKey pubKey = kf.generatePublic(pubKeySpec);
+ c.init(Cipher.ENCRYPT_MODE, pubKey);
+
+ assertEquals("RSA is not a block cipher and should return block size of 0",
+ 0, c.getBlockSize());
+ }
+
+ public void testRSA_ECB_NoPadding_GetOutputSize_NoInit_Failure() throws Exception {
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+ try {
+ c.getOutputSize(RSA_2048_Vector1.length);
+ fail("Should throw IllegalStateException if getOutputSize is called before init");
+ } catch (IllegalStateException success) {
+ }
+ }
+
+ public void testRSA_ECB_NoPadding_GetOutputSize_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ final PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+ c.init(Cipher.ENCRYPT_MODE, pubKey);
+
+ final int modulusInBytes = RSA_2048_modulus.bitLength() / 8;
+ assertEquals("Output size should be equal to modulus size",
+ modulusInBytes, c.getOutputSize(RSA_2048_Vector1.length));
+
+ assertEquals("Output size should be equal to modulus size",
+ modulusInBytes, c.getOutputSize(RSA_2048_Vector1.length * 2));
+
+ assertEquals("Output size should be equal to modulus size",
+ modulusInBytes, c.getOutputSize(0));
+ }
+
+ public void testRSA_ECB_NoPadding_GetIV_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ final PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+ assertNull("ECB mode has no IV and should be null", c.getIV());
+
+ c.init(Cipher.ENCRYPT_MODE, pubKey);
+
+ assertNull("ECB mode has no IV and should be null", c.getIV());
+ }
+
+ public void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success() throws Exception {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+ RSA_2048_publicExponent);
+ final PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+ Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
+ assertNull("Parameters should be null", c.getParameters());
+ }
}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index f8a744d..a015d19 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -386,6 +386,51 @@
c.close();
}
+ /**
+ * http://code.google.com/p/android/issues/detail?id=31903
+ * This test case directly tests the fix for the issue.
+ */
+ public void test_SSLEngine_clientAuthWantedNoClientCert() throws Exception {
+ TestSSLContext clientAuthContext
+ = TestSSLContext.create(TestKeyStore.getClient(),
+ TestKeyStore.getServer());
+ TestSSLEnginePair p = TestSSLEnginePair.create(clientAuthContext,
+ new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ server.setWantClientAuth(true);
+ }
+ });
+ assertConnected(p);
+ clientAuthContext.close();
+ }
+
+ /**
+ * http://code.google.com/p/android/issues/detail?id=31903
+ * This test case verifies that if the server requires a client cert
+ * (setNeedClientAuth) but the client does not provide one SSL connection
+ * establishment will fail
+ */
+ public void test_SSLEngine_clientAuthNeededNoClientCert() throws Exception {
+ boolean handshakeExceptionCaught = false;
+ TestSSLContext clientAuthContext
+ = TestSSLContext.create(TestKeyStore.getClient(),
+ TestKeyStore.getServer());
+ try {
+ TestSSLEnginePair.create(clientAuthContext,
+ new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ server.setNeedClientAuth(true);
+ }
+ });
+ fail();
+ } catch (SSLHandshakeException expected) {
+ } finally {
+ clientAuthContext.close();
+ }
+ }
+
public void test_SSLEngine_getEnableSessionCreation() throws Exception {
TestSSLContext c = TestSSLContext.create();
SSLEngine e = c.clientContext.createSSLEngine();
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
index 90cdeb9..8c9239e 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -16,6 +16,7 @@
package libcore.javax.net.ssl;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
@@ -30,6 +31,10 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.KeyManager;
@@ -38,7 +43,6 @@
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
@@ -271,32 +275,28 @@
SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ server.startHandshake();
+ assertNotNull(server.getSession());
try {
- server.startHandshake();
- assertNotNull(server.getSession());
- try {
- server.getSession().getPeerCertificates();
- fail();
- } catch (SSLPeerUnverifiedException expected) {
- }
- Certificate[] localCertificates = server.getSession().getLocalCertificates();
- assertNotNull(localCertificates);
- TestKeyStore.assertChainLength(localCertificates);
- assertNotNull(localCertificates[0]);
- TestSSLContext.assertServerCertificateChain(c.serverTrustManager,
- localCertificates);
- TestSSLContext.assertCertificateInKeyStore(localCertificates[0],
- c.serverKeyStore);
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
+ server.getSession().getPeerCertificates();
+ fail();
+ } catch (SSLPeerUnverifiedException expected) {
}
+ Certificate[] localCertificates = server.getSession().getLocalCertificates();
+ assertNotNull(localCertificates);
+ TestKeyStore.assertChainLength(localCertificates);
+ assertNotNull(localCertificates[0]);
+ TestSSLContext.assertServerCertificateChain(c.serverTrustManager,
+ localCertificates);
+ TestSSLContext.assertCertificateInKeyStore(localCertificates[0],
+ c.serverKeyStore);
+ return null;
}
});
- thread.start();
+ executor.shutdown();
client.startHandshake();
assertNotNull(client.getSession());
assertNull(client.getSession().getLocalCertificates());
@@ -307,7 +307,7 @@
TestSSLContext.assertServerCertificateChain(c.clientTrustManager,
peerCertificates);
TestSSLContext.assertCertificateInKeyStore(peerCertificates[0], c.serverKeyStore);
- thread.join();
+ future.get();
client.close();
server.close();
c.close();
@@ -321,25 +321,24 @@
// RI used to throw SSLException on accept, now throws on startHandshake
if (StandardNames.IS_RI) {
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
try {
server.startHandshake();
+ fail();
} catch (SSLHandshakeException expected) {
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
}
+ return null;
}
});
- thread.start();
+ executor.shutdown();
try {
client.startHandshake();
fail();
} catch (SSLHandshakeException expected) {
}
- thread.join();
+ future.get();
server.close();
} else {
try {
@@ -359,20 +358,16 @@
SSLSocket client = (SSLSocket)
clientContext.getSocketFactory().createSocket(c.host, c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- server.startHandshake();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ server.startHandshake();
+ return null;
}
});
- thread.start();
+ executor.shutdown();
client.startHandshake();
- thread.join();
+ future.get();
client.close();
server.close();
c.close();
@@ -383,18 +378,14 @@
final SSLSocket client = (SSLSocket)
c.clientContext.getSocketFactory().createSocket(c.host, c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- server.startHandshake();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ server.startHandshake();
+ return null;
}
});
- thread.start();
+ executor.shutdown();
final boolean[] handshakeCompletedListenerCalled = new boolean[1];
client.addHandshakeCompletedListener(new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent event) {
@@ -472,7 +463,7 @@
}
});
client.startHandshake();
- thread.join();
+ future.get();
if (!TestSSLContext.sslServerSocketSupportsSessionTickets()) {
assertNotNull(c.serverContext.getServerSessionContext().getSession(
client.getSession().getId()));
@@ -492,25 +483,21 @@
final SSLSocket client = (SSLSocket)
c.clientContext.getSocketFactory().createSocket(c.host, c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- server.startHandshake();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ server.startHandshake();
+ return null;
}
});
- thread.start();
+ executor.shutdown();
client.addHandshakeCompletedListener(new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent event) {
throw new RuntimeException("RuntimeException from handshakeCompleted");
}
});
client.startHandshake();
- thread.join();
+ future.get();
client.close();
server.close();
c.close();
@@ -551,6 +538,46 @@
}
}
+ private void test_SSLSocket_setUseClientMode(final boolean clientClientMode,
+ final boolean serverClientMode)
+ throws Exception {
+ TestSSLContext c = TestSSLContext.create();
+ SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
+ c.port);
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<IOException> future = executor.submit(new Callable<IOException>() {
+ @Override public IOException call() throws Exception {
+ try {
+ if (!serverClientMode) {
+ server.setSoTimeout(1 * 1000);
+ }
+ server.setUseClientMode(serverClientMode);
+ server.startHandshake();
+ return null;
+ } catch (SSLHandshakeException e) {
+ return e;
+ } catch (SocketTimeoutException e) {
+ return e;
+ }
+ }
+ });
+ executor.shutdown();
+ if (!clientClientMode) {
+ client.setSoTimeout(1 * 1000);
+ }
+ client.setUseClientMode(clientClientMode);
+ client.startHandshake();
+ IOException ioe = future.get();
+ if (ioe != null) {
+ throw ioe;
+ }
+ client.close();
+ server.close();
+ c.close();
+ }
+
public void test_SSLSocket_setUseClientMode_afterHandshake() throws Exception {
// can't set after handshake
@@ -567,72 +594,25 @@
}
}
- private void test_SSLSocket_setUseClientMode(final boolean clientClientMode,
- final boolean serverClientMode)
- throws Exception {
- TestSSLContext c = TestSSLContext.create();
- SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
- c.port);
- final SSLSocket server = (SSLSocket) c.serverSocket.accept();
-
- final SSLHandshakeException[] sslHandshakeException = new SSLHandshakeException[1];
- final SocketTimeoutException[] socketTimeoutException = new SocketTimeoutException[1];
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- if (!serverClientMode) {
- server.setSoTimeout(1 * 1000);
- }
- server.setUseClientMode(serverClientMode);
- server.startHandshake();
- } catch (SSLHandshakeException e) {
- sslHandshakeException[0] = e;
- } catch (SocketTimeoutException e) {
- socketTimeoutException[0] = e;
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- });
- thread.start();
- if (!clientClientMode) {
- client.setSoTimeout(1 * 1000);
- }
- client.setUseClientMode(clientClientMode);
- client.startHandshake();
- thread.join();
- if (sslHandshakeException[0] != null) {
- throw sslHandshakeException[0];
- }
- if (socketTimeoutException[0] != null) {
- throw socketTimeoutException[0];
- }
- client.close();
- server.close();
- c.close();
- }
-
public void test_SSLSocket_untrustedServer() throws Exception {
TestSSLContext c = TestSSLContext.create(TestKeyStore.getClientCA2(),
TestKeyStore.getServer());
SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
try {
server.startHandshake();
+ assertFalse(StandardNames.IS_RI);
} catch (SSLHandshakeException expected) {
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
+ assertTrue(StandardNames.IS_RI);
}
+ return null;
}
});
- thread.start();
+ executor.shutdown();
try {
client.startHandshake();
fail();
@@ -641,7 +621,7 @@
}
client.close();
server.close();
- thread.join();
+ future.get();
}
public void test_SSLSocket_clientAuth() throws Exception {
@@ -650,43 +630,38 @@
SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- assertFalse(server.getWantClientAuth());
- assertFalse(server.getNeedClientAuth());
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ assertFalse(server.getWantClientAuth());
+ assertFalse(server.getNeedClientAuth());
- // confirm turning one on by itself
- server.setWantClientAuth(true);
- assertTrue(server.getWantClientAuth());
- assertFalse(server.getNeedClientAuth());
+ // confirm turning one on by itself
+ server.setWantClientAuth(true);
+ assertTrue(server.getWantClientAuth());
+ assertFalse(server.getNeedClientAuth());
- // confirm turning setting on toggles the other
- server.setNeedClientAuth(true);
- assertFalse(server.getWantClientAuth());
- assertTrue(server.getNeedClientAuth());
+ // confirm turning setting on toggles the other
+ server.setNeedClientAuth(true);
+ assertFalse(server.getWantClientAuth());
+ assertTrue(server.getNeedClientAuth());
- // confirm toggling back
- server.setWantClientAuth(true);
- assertTrue(server.getWantClientAuth());
- assertFalse(server.getNeedClientAuth());
+ // confirm toggling back
+ server.setWantClientAuth(true);
+ assertTrue(server.getWantClientAuth());
+ assertFalse(server.getNeedClientAuth());
- server.startHandshake();
-
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ server.startHandshake();
+ return null;
}
});
- thread.start();
+ executor.shutdown();
client.startHandshake();
assertNotNull(client.getSession().getLocalCertificates());
TestKeyStore.assertChainLength(client.getSession().getLocalCertificates());
TestSSLContext.assertClientCertificateChain(c.clientTrustManager,
client.getSession().getLocalCertificates());
- thread.join();
+ future.get();
client.close();
server.close();
c.close();
@@ -727,22 +702,20 @@
SSLSocket client = (SSLSocket) clientContext.getSocketFactory().createSocket(c.host,
c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
try {
server.setNeedClientAuth(true);
server.startHandshake();
fail();
} catch (SSLHandshakeException expected) {
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
}
+ return null;
}
});
- thread.start();
+ executor.shutdown();
try {
client.startHandshake();
fail();
@@ -750,7 +723,7 @@
// before we would get a NullPointerException from passing
// due to the null PrivateKey return by the X509KeyManager.
}
- thread.join();
+ future.get();
client.close();
server.close();
c.close();
@@ -773,29 +746,25 @@
SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ server.setEnableSessionCreation(false);
try {
- server.setEnableSessionCreation(false);
- try {
- server.startHandshake();
- fail();
- } catch (SSLException expected) {
- }
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
+ server.startHandshake();
+ fail();
+ } catch (SSLException expected) {
}
+ return null;
}
});
- thread.start();
+ executor.shutdown();
try {
client.startHandshake();
fail();
} catch (SSLException expected) {
}
- thread.join();
+ future.get();
client.close();
server.close();
c.close();
@@ -806,29 +775,25 @@
SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
- Thread thread = new Thread(new Runnable () {
- public void run() {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
try {
- try {
- server.startHandshake();
- fail();
- } catch (SSLException expected) {
- }
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
+ server.startHandshake();
+ fail();
+ } catch (SSLException expected) {
}
+ return null;
}
});
- thread.start();
+ executor.shutdown();
client.setEnableSessionCreation(false);
try {
client.startHandshake();
fail();
} catch (SSLException expected) {
}
- thread.join();
+ future.get();
client.close();
server.close();
c.close();
@@ -1012,32 +977,25 @@
c.host.getHostName(),
c.port,
false);
- Thread clientThread = new Thread(new Runnable () {
- public void run() {
- try {
- try {
- wrapping.startHandshake();
- wrapping.getOutputStream().write(42);
- // close the underlying socket,
- // so that no SSL shutdown is sent
- underlying.close();
- wrapping.close();
- } catch (SSLException expected) {
- }
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> clientFuture = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ wrapping.startHandshake();
+ wrapping.getOutputStream().write(42);
+ // close the underlying socket,
+ // so that no SSL shutdown is sent
+ underlying.close();
+ wrapping.close();
+ return null;
}
});
- clientThread.start();
+ executor.shutdown();
SSLSocket server = (SSLSocket) c.serverSocket.accept();
server.startHandshake();
server.getInputStream().read();
// wait for thread to finish so we know client is closed.
- clientThread.join();
+ clientFuture.get();
// close should cause an SSL_shutdown which will fail
// because the peer has closed, but it shouldn't throw.
server.close();
@@ -1054,9 +1012,10 @@
assertEquals(0, wrapping.getSoTimeout());
// setting wrapper sets underlying and ...
- wrapping.setSoTimeout(10);
- assertEquals(10, wrapping.getSoTimeout());
- assertEquals(10, underlying.getSoTimeout());
+ int expectedTimeoutMillis = 1000; // Using a small value such as 10 was affected by rounding
+ wrapping.setSoTimeout(expectedTimeoutMillis);
+ assertEquals(expectedTimeoutMillis, wrapping.getSoTimeout());
+ assertEquals(expectedTimeoutMillis, underlying.getSoTimeout());
// ... getting wrapper inspects underlying
underlying.setSoTimeout(0);
@@ -1126,17 +1085,15 @@
private void test_SSLSocket_interrupt_case(Socket toRead, final Socket toClose)
throws Exception {
- new Thread() {
- @Override
- public void run() {
- try {
- Thread.sleep(1 * 1000);
- toClose.close();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ Thread.sleep(1 * 1000);
+ toClose.close();
+ return null;
}
- }.start();
+ });
+ executor.shutdown();
try {
toRead.setSoTimeout(5 * 1000);
toRead.getInputStream().read();
@@ -1145,6 +1102,43 @@
throw e;
} catch (SocketException expected) {
}
+ future.get();
+ }
+
+ /**
+ * b/7014266 Test to confirm that an SSLSocket.close() on one
+ * thread will interupt another thread blocked reading on the same
+ * socket.
+ */
+ public void test_SSLSocket_interrupt_read() throws Exception {
+ TestSSLContext c = TestSSLContext.create();
+ final Socket underlying = new Socket(c.host, c.port);
+ final SSLSocket wrapping = (SSLSocket)
+ c.clientContext.getSocketFactory().createSocket(underlying,
+ c.host.getHostName(),
+ c.port,
+ false);
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> clientFuture = executor.submit(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try {
+ wrapping.startHandshake();
+ assertFalse(StandardNames.IS_RI);
+ wrapping.setSoTimeout(5 * 1000);
+ assertEquals(-1, wrapping.getInputStream().read());
+ } catch (Exception e) {
+ assertTrue(StandardNames.IS_RI);
+ }
+ return null;
+ }
+ });
+ executor.shutdown();
+
+ SSLSocket server = (SSLSocket) c.serverSocket.accept();
+ server.startHandshake();
+ wrapping.close();
+ clientFuture.get();
+ server.close();
}
public void test_TestSSLSocketPair_create() {
diff --git a/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java b/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
index 360ca44..133924e 100644
--- a/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
+++ b/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
@@ -959,7 +959,7 @@
HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
connection.addRequestProperty("Cache-Control", "only-if-cached");
- assertBadGateway(connection);
+ assertGatewayTimeout(connection);
}
public void testRequestOnlyIfCachedWithFullResponseCached() throws IOException {
@@ -983,7 +983,7 @@
assertEquals("A", readAscii(server.getUrl("/").openConnection()));
HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
connection.addRequestProperty("Cache-Control", "only-if-cached");
- assertBadGateway(connection);
+ assertGatewayTimeout(connection);
}
public void testRequestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
@@ -993,7 +993,7 @@
assertEquals("A", readAscii(server.getUrl("/").openConnection()));
HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
connection.addRequestProperty("Cache-Control", "only-if-cached");
- assertBadGateway(connection);
+ assertGatewayTimeout(connection);
}
public void testRequestCacheControlNoCache() throws Exception {
@@ -1804,13 +1804,13 @@
}
}
- private void assertBadGateway(HttpURLConnection connection) throws IOException {
+ private void assertGatewayTimeout(HttpURLConnection connection) throws IOException {
try {
connection.getInputStream();
fail();
} catch (FileNotFoundException expected) {
}
- assertEquals(HttpURLConnection.HTTP_BAD_GATEWAY, connection.getResponseCode());
+ assertEquals(504, connection.getResponseCode());
assertEquals(-1, connection.getErrorStream().read());
}
diff --git a/luni/src/test/java/libcore/xml/DomSerializationTest.java b/luni/src/test/java/libcore/xml/DomSerializationTest.java
new file mode 100644
index 0000000..9015f97
--- /dev/null
+++ b/luni/src/test/java/libcore/xml/DomSerializationTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.xml;
+
+import java.io.StringWriter;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import junit.framework.TestCase;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public final class DomSerializationTest extends TestCase {
+ private DocumentBuilder documentBuilder;
+ private Transformer transformer;
+
+ @Override protected void setUp() throws Exception {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ documentBuilder = builderFactory.newDocumentBuilder();
+ transformer = TransformerFactory.newInstance().newTransformer();
+ }
+
+ public void testWriteDocument() throws Exception {
+ Document document = documentBuilder.newDocument();
+ Element foo = document.createElement("foo");
+ Attr quux = document.createAttribute("quux");
+ quux.setValue("abc");
+ foo.setAttributeNode(quux);
+ foo.appendChild(document.createElement("bar"));
+ foo.appendChild(document.createElement("baz"));
+ document.appendChild(foo);
+ assertXmlEquals("<foo quux=\"abc\"><bar/><baz/></foo>", document);
+ }
+
+ public void testWriteSpecialCharactersInText() throws Exception {
+ Document document = documentBuilder.newDocument();
+ Element foo = document.createElement("foo");
+ foo.appendChild(document.createTextNode("5'8\", 5 < 6 & 7 > 3!"));
+ document.appendChild(foo);
+ assertXmlEquals("<foo>5'8\", 5 < 6 & 7 > 3!</foo>", document);
+ }
+
+ private String toXml(Document document) throws Exception {
+ StringWriter stringWriter = new StringWriter();
+ transformer.transform(new DOMSource(document), new StreamResult(stringWriter));
+ return stringWriter.toString();
+ }
+
+ private void assertXmlEquals(String expectedXml, Document document) throws Exception {
+ String actual = toXml(document);
+ String declarationA = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ String declarationB = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
+ assertTrue(actual, actual.equals(declarationA + expectedXml)
+ || actual.equals(declarationB + expectedXml));
+ }
+}
diff --git a/xml/src/test/java/org/kxml2/io/KXmlSerializerTest.java b/luni/src/test/java/libcore/xml/KxmlSerializerTest.java
similarity index 63%
rename from xml/src/test/java/org/kxml2/io/KXmlSerializerTest.java
rename to luni/src/test/java/libcore/xml/KxmlSerializerTest.java
index 7aba746..6a75a9b 100644
--- a/xml/src/test/java/org/kxml2/io/KXmlSerializerTest.java
+++ b/luni/src/test/java/libcore/xml/KxmlSerializerTest.java
@@ -14,26 +14,69 @@
* limitations under the License.
*/
-package org.kxml2.io;
-
-import junit.framework.TestCase;
+package libcore.xml;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
+import junit.framework.TestCase;
+import org.kxml2.io.KXmlSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlSerializer;
+import static tests.support.Support_Xml.domOf;
-import static tests.support.Support_Xml.*;
-
-public class KXmlSerializerTest extends TestCase {
+public final class KxmlSerializerTest extends TestCase {
private static final String NAMESPACE = null;
- private static boolean isValidXmlCodePoint(int c) {
- // http://www.w3.org/TR/REC-xml/#charsets
- return (c >= 0x20 && c <= 0xd7ff) || (c == 0x9) || (c == 0xa) || (c == 0xd) ||
- (c >= 0xe000 && c <= 0xfffd) || (c >= 0x10000 && c <= 0x10ffff);
+ public void testWhitespaceInAttributeValue() throws Exception {
+ StringWriter stringWriter = new StringWriter();
+ XmlSerializer serializer = new KXmlSerializer();
+ serializer.setOutput(stringWriter);
+ serializer.startDocument("UTF-8", null);
+ serializer.startTag(NAMESPACE, "a");
+ serializer.attribute(NAMESPACE, "cr", "\r");
+ serializer.attribute(NAMESPACE, "lf", "\n");
+ serializer.attribute(NAMESPACE, "tab", "\t");
+ serializer.attribute(NAMESPACE, "space", " ");
+ serializer.endTag(NAMESPACE, "a");
+ serializer.endDocument();
+ assertXmlEquals("<a cr=\" \" lf=\" \" tab=\"	\" space=\" \" />",
+ stringWriter.toString());
+ }
+
+ public void testWriteDocument() throws Exception {
+ StringWriter stringWriter = new StringWriter();
+ XmlSerializer serializer = new KXmlSerializer();
+ serializer.setOutput(stringWriter);
+ serializer.startDocument("UTF-8", null);
+ serializer.startTag(NAMESPACE, "foo");
+ serializer.attribute(NAMESPACE, "quux", "abc");
+ serializer.startTag(NAMESPACE, "bar");
+ serializer.endTag(NAMESPACE, "bar");
+ serializer.startTag(NAMESPACE, "baz");
+ serializer.endTag(NAMESPACE, "baz");
+ serializer.endTag(NAMESPACE, "foo");
+ serializer.endDocument();
+ assertXmlEquals("<foo quux=\"abc\"><bar /><baz /></foo>", stringWriter.toString());
+ }
+
+ // http://code.google.com/p/android/issues/detail?id=21250
+ public void testWriteSpecialCharactersInText() throws Exception {
+ StringWriter stringWriter = new StringWriter();
+ XmlSerializer serializer = new KXmlSerializer();
+ serializer.setOutput(stringWriter);
+ serializer.startDocument("UTF-8", null);
+ serializer.startTag(NAMESPACE, "foo");
+ serializer.text("5'8\", 5 < 6 & 7 > 3!");
+ serializer.endTag(NAMESPACE, "foo");
+ serializer.endDocument();
+ assertXmlEquals("<foo>5'8\", 5 < 6 & 7 > 3!</foo>", stringWriter.toString());
+ }
+
+ private void assertXmlEquals(String expectedXml, String actualXml) throws Exception {
+ String declaration = "<?xml version='1.0' encoding='UTF-8' ?>";
+ assertEquals(declaration + expectedXml, actualXml);
}
private static XmlSerializer newSerializer() throws IOException {
@@ -116,4 +159,10 @@
}
assertEquals("a]]>b", text);
}
+
+ private static boolean isValidXmlCodePoint(int c) {
+ // http://www.w3.org/TR/REC-xml/#charsets
+ return (c >= 0x20 && c <= 0xd7ff) || (c == 0x9) || (c == 0xa) || (c == 0xd) ||
+ (c >= 0xe000 && c <= 0xfffd) || (c >= 0x10000 && c <= 0x10ffff);
+ }
}
diff --git a/luni/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTest.java b/luni/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTest.java
index 33ce8fb..8395c00 100644
--- a/luni/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTest.java
+++ b/luni/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTest.java
@@ -126,6 +126,17 @@
m2.getDeclaredAnnotations()[0].hashCode());
}
+
+ public static void test35304() throws Exception {
+ Class c = AnnotationTest.class;
+ Class[] parameterTypes = new Class[] { String.class, String.class };
+ Annotation[][] annotations = c.getDeclaredMethod("test35304_method", parameterTypes).getParameterAnnotations();
+ assertEquals(2, annotations.length); // Two parameters.
+ assertEquals(0, annotations[0].length); // No annotations on the first.
+ assertEquals(1, annotations[1].length); // One annotation on the second.
+ }
+
+ private static String test35304_method(String s1, @Deprecated String s2) { return null; }
}
class AnnotatedClass2 {
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java
index b3b2931..3ea57bf 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java
@@ -33,6 +33,7 @@
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchProviderException;
+import java.util.ArrayList;
import java.util.Arrays;
import javax.crypto.Cipher;
@@ -43,6 +44,8 @@
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import libcore.util.SerializationTester;
+
/**
*/
public class SealedObjectTest extends TestCase {
@@ -291,4 +294,23 @@
}
}
+ // http://code.google.com/p/android/issues/detail?id=4834
+ public void testDeserialization() throws Exception {
+ // (Boilerplate so we can create SealedObject instances.)
+ KeyGenerator kg = KeyGenerator.getInstance("DES");
+ Key key = kg.generateKey();
+ Cipher cipher = Cipher.getInstance("DES");
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+
+ // Incorrect use of readUnshared meant you couldn't have two SealedObjects
+ // with the same algorithm or parameters algorithm...
+ ArrayList<SealedObject> sealedObjects = new ArrayList<SealedObject>();
+ for (int i = 0; i < 10; ++i) {
+ sealedObjects.add(new SealedObject("hello", cipher));
+ }
+ String serializedForm = SerializationTester.serializeHex(sealedObjects);
+
+ // ...so this would throw "java.io.InvalidObjectException: Unshared read of back reference".
+ SerializationTester.deserializeHex(serializedForm);
+ }
}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherRSATest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherRSATest.java
index d25d958..0e070f6 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherRSATest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherRSATest.java
@@ -15,8 +15,6 @@
*/
package org.apache.harmony.crypto.tests.javax.crypto.func;
-import dalvik.annotation.AndroidOnly;
-
import junit.framework.TestCase;
public class CipherRSATest extends TestCase {
@@ -55,7 +53,6 @@
assertEquals(rsa.getFailureMessages(), 0, rsa.getTotalFailuresNumber());
}
- @AndroidOnly("Fails on RI but succeeds on Android.")
public void test_RSANoPadding() {
CipherRSAThread rsa = new CipherRSAThread("RSA", new int[] {1024},
new String[] {"ECB"}, new String[] {"NOPADDING"});
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherRSAThread.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherRSAThread.java
index f7445b1..31e1075 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherRSAThread.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherRSAThread.java
@@ -45,6 +45,10 @@
cip.init(Cipher.DECRYPT_MODE, kp.getPrivate());
cip.doFinal(output, 0, outputSize, decrypted);
- checkEncodedData(input, decrypted);
+ if ("NOPADDING".equals(getPadding())) {
+ checkPaddedEncodedData(input, decrypted, outputSize - input.length);
+ } else {
+ checkEncodedData(input, decrypted);
+ }
}
}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherThread.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherThread.java
index 4dac176..2fd388b 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherThread.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/func/CipherThread.java
@@ -57,6 +57,20 @@
}
}
+ public void checkPaddedEncodedData(byte[] original, byte[] encoded, int offset)
+ throws Exception {
+ for (int i = 0; i < offset; i++) {
+ if (encoded[i] != 0) {
+ throw new Exception("Encoded data is not properly padded at offset " + i);
+ }
+ }
+ for (int i = 0; i < original.length; i++) {
+ if (original[i] != encoded[i + offset]) {
+ throw new Exception("Source and encoded data not match " + getCipherParameters());
+ }
+ }
+ }
+
public void launcher() {
Thread thread = null;
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
index 21832e7..d1674f3 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
@@ -19,7 +19,6 @@
import dalvik.annotation.BrokenTest;
import junit.framework.TestCase;
import tests.support.Support_Configuration;
-import tests.support.Support_PortManager;
import tests.support.Support_TestWebData;
import tests.support.Support_TestWebServer;
import tests.support.resource.Support_Resources;
@@ -220,9 +219,8 @@
public void setUp() throws Exception {
super.setUp();
- port = Support_PortManager.getNextPort();
server = new Support_TestWebServer();
- server.initServer(port, false);
+ port = server.initServer();
url = new URL("http://localhost:" + port + "/test1");
uc = url.openConnection();
url2 = new URL("http://localhost:" + port + "/test2");
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore2Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore2Test.java
index fd27edc..d303903 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore2Test.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore2Test.java
@@ -49,6 +49,7 @@
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
+import libcore.java.security.StandardNames;
import tests.support.Support_TestProvider;
public class KeyStore2Test extends junit.framework.TestCase {
@@ -817,8 +818,9 @@
try {
keyTest.setEntry("alias", pke, null);
- fail();
- } catch (Exception expected) {
+ assertFalse(StandardNames.IS_RI); // BKS KeyStore does not require a password
+ } catch (KeyStoreException e) {
+ assertTrue(StandardNames.IS_RI); // JKS KeyStore requires a password
}
keyTest.setEntry("alias", pke, pp);
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreSpiTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreSpiTest.java
index 4fdddbb..a85459b 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreSpiTest.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreSpiTest.java
@@ -15,21 +15,15 @@
* limitations under the License.
*/
-/**
- * @author Vera Y. Petrashkova
- * @version $Revision$
- */
-
package org.apache.harmony.security.tests.java.security;
-import junit.framework.TestCase;
-
-import org.apache.harmony.security.tests.support.MyKeyStoreSpi;
-import org.apache.harmony.security.tests.support.MyLoadStoreParams;
-
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStore.LoadStoreParameter;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
@@ -39,16 +33,15 @@
import java.security.SignatureException;
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
-import java.security.KeyStore.LoadStoreParameter;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.util.Date;
+import javax.crypto.SecretKey;
+import junit.framework.TestCase;
+import org.apache.harmony.security.tests.support.MyKeyStoreSpi;
+import org.apache.harmony.security.tests.support.MyLoadStoreParams;
-/**
- * Tests for <code>KeyStoreSpi</code> constructor and methods
- *
- */
public class KeyStoreSpiTest extends TestCase {
@SuppressWarnings("cast")
@@ -86,62 +79,73 @@
try {
assertFalse(ksSpi.engineEntryInstanceOf(null,
KeyStore.TrustedCertificateEntry.class));
- } catch (NullPointerException e) {
- // ok
+ } catch (NullPointerException expected) {
}
try {
assertFalse(ksSpi.engineEntryInstanceOf(
"test_engineEntryInstanceOf_Alias1", null));
- } catch (NullPointerException e) {
- // ok
+ } catch (NullPointerException expected) {
}
}
- public void testKeyStoteSpi01() throws IOException,
+ public void testKeyStoreSpi01() throws IOException,
NoSuchAlgorithmException, CertificateException,
UnrecoverableEntryException, KeyStoreException {
- KeyStoreSpi ksSpi = new MyKeyStoreSpi();
+ final boolean[] keyEntryWasSet = new boolean[1];
+ KeyStoreSpi ksSpi = new MyKeyStoreSpi() {
+ @Override public void engineSetKeyEntry(String alias, Key key, char[] password,
+ Certificate[] chain) throws KeyStoreException { keyEntryWasSet[0] = true; }
+ };
- tmpEntry entry = new tmpEntry();
- tmpProtection pPar = new tmpProtection();
+ BadKeyStoreEntry badEntry = new BadKeyStoreEntry();
+ BadKeyStoreProtectionParameter badParameter = new BadKeyStoreProtectionParameter();
+
+ KeyStore.SecretKeyEntry dummyEntry = new KeyStore.SecretKeyEntry(new SecretKey() {
+ @Override public String getAlgorithm() { return null; }
+ @Override public String getFormat() { return null; }
+ @Override public byte[] getEncoded() { return null; }
+ });
try {
ksSpi.engineStore(null);
- } catch (UnsupportedOperationException e) {
+ } catch (UnsupportedOperationException expected) {
}
assertNull("Not null entry", ksSpi.engineGetEntry("aaa", null));
- assertNull("Not null entry", ksSpi.engineGetEntry(null, pPar));
- assertNull("Not null entry", ksSpi.engineGetEntry("aaa", pPar));
+ assertNull("Not null entry", ksSpi.engineGetEntry(null, badParameter));
+ assertNull("Not null entry", ksSpi.engineGetEntry("aaa", badParameter));
try {
ksSpi.engineSetEntry("", null, null);
fail("KeyStoreException or NullPointerException must be thrown");
- } catch (KeyStoreException e) {
- } catch (NullPointerException e) {
+ } catch (KeyStoreException expected) {
+ } catch (NullPointerException expected) {
}
try {
ksSpi.engineSetEntry("", new KeyStore.TrustedCertificateEntry(
new MyCertificate("type", new byte[0])), null);
fail("KeyStoreException must be thrown");
- } catch (KeyStoreException e) {
+ } catch (KeyStoreException expected) {
}
try {
- ksSpi.engineSetEntry("aaa", entry, null);
+ ksSpi.engineSetEntry("aaa", badEntry, null);
fail("KeyStoreException must be thrown");
- } catch (KeyStoreException e) {
+ } catch (KeyStoreException expected) {
}
+
+ ksSpi.engineSetEntry("aaa", dummyEntry, null);
+ assertTrue(keyEntryWasSet[0]);
}
/**
* Test for <code>KeyStoreSpi()</code> constructor and abstract engine
* methods. Assertion: creates new KeyStoreSpi object.
*/
- public void testKeyStoteSpi02() throws NoSuchAlgorithmException,
+ public void testKeyStoreSpi02() throws NoSuchAlgorithmException,
UnrecoverableKeyException, CertificateException {
KeyStoreSpi ksSpi = new MyKeyStoreSpi();
assertNull("engineGetKey(..) must return null", ksSpi.engineGetKey("",
@@ -155,23 +159,23 @@
try {
ksSpi.engineSetKeyEntry("", null, new char[0], new Certificate[0]);
fail("KeyStoreException must be thrown from engineSetKeyEntry(..)");
- } catch (KeyStoreException e) {
+ } catch (KeyStoreException expected) {
}
try {
ksSpi.engineSetKeyEntry("", new byte[0], new Certificate[0]);
fail("KeyStoreException must be thrown from engineSetKeyEntry(..)");
- } catch (KeyStoreException e) {
+ } catch (KeyStoreException expected) {
}
try {
ksSpi.engineSetCertificateEntry("", null);
fail("KeyStoreException must be thrown "
+ "from engineSetCertificateEntry(..)");
- } catch (KeyStoreException e) {
+ } catch (KeyStoreException expected) {
}
try {
ksSpi.engineDeleteEntry("");
fail("KeyStoreException must be thrown from engineDeleteEntry(..)");
- } catch (KeyStoreException e) {
+ } catch (KeyStoreException expected) {
}
assertNull("engineAliases() must return null", ksSpi.engineAliases());
assertFalse("engineContainsAlias(..) must return false", ksSpi
@@ -180,7 +184,7 @@
try {
ksSpi.engineStore(null, null);
fail("IOException must be thrown");
- } catch (IOException e) {
+ } catch (IOException expected) {
}
}
@@ -202,35 +206,30 @@
try {
ksSpi.engineLoad(null);
fail("Should throw exception");
- } catch (RuntimeException e) {
- assertSame(msg, e.getMessage());
+ } catch (RuntimeException expected) {
+ assertSame(msg, expected.getMessage());
}
// test: protection parameter is null
try {
ksSpi.engineLoad(new MyLoadStoreParams(null));
fail("No expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
+ } catch (UnsupportedOperationException expected) {
}
// test: protection parameter is not instanceof
// PasswordProtection or CallbackHandlerProtection
try {
- ksSpi.engineLoad(new MyLoadStoreParams(new tmpProtection()));
+ ksSpi.engineLoad(new MyLoadStoreParams(new BadKeyStoreProtectionParameter()));
fail("No expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
+ } catch (UnsupportedOperationException expected) {
}
}
}
-/**
- * Additional class implements KeyStore.Entry interface
- */
-class tmpEntry implements KeyStore.Entry {
-}
-
-class tmpProtection implements KeyStore.ProtectionParameter {
-}
+// These are "Bad" because they are not expected inner subclasses of the KeyStore class.
+class BadKeyStoreEntry implements Entry {}
+class BadKeyStoreProtectionParameter implements ProtectionParameter {}
@SuppressWarnings("unused")
class MyCertificate extends Certificate {
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/SecureRandom2Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/SecureRandom2Test.java
index cf030c7..aa0ec67 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/SecureRandom2Test.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/SecureRandom2Test.java
@@ -301,40 +301,15 @@
* as it tends to be error prone and open up security holes.
* See {@link SecureRandom} for more details about insecure seeding.
*
- * @see #testSameSeedGeneratesSameResultsWhenConstructorIsUsed()
+ * Note that this only works with the Harmony "Crypto" provider.
*/
public void testSameSeedGeneratesSameResults() throws Exception {
byte[] seed1 = { 'a', 'b', 'c' };
- SecureRandom sr1 = SecureRandom.getInstance("SHA1PRNG");
+ SecureRandom sr1 = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr1.setSeed(seed1);
byte[] seed2 = { 'a', 'b', 'c' };
- SecureRandom sr2 = SecureRandom.getInstance("SHA1PRNG");
- sr2.setSeed(seed2);
-
- assertTrue(sr1.nextLong() == sr2.nextLong());
- }
-
- /**
- * Same tests as {@link #testSameSeedGeneratesSameResults()}, except
- * here we use the constructor, not {@link SecureRandom#getInstance(String)}.
- *
- * Note that Android behaves differently than the reference implementation.
- * This test fails on the reference implementation.
- *
- * In the future, it may make sense to change our implementation to
- * match the reference implementation. It may also make sense to
- * disallow seeding {@code SecureRandom} completely, as it tends to
- * be error prone and open up security holes. See {@link SecureRandom}
- * for more details about insecure seeding.
- */
- public void testSameSeedGeneratesSameResultsWhenConstructorIsUsed() {
- byte[] seed1 = { 'a', 'b', 'c' };
- SecureRandom sr1 = new SecureRandom();
- sr1.setSeed(seed1);
-
- byte[] seed2 = { 'a', 'b', 'c' };
- SecureRandom sr2 = new SecureRandom();
+ SecureRandom sr2 = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr2.setSeed(seed2);
assertTrue(sr1.nextLong() == sr2.nextLong());
@@ -348,10 +323,12 @@
* SHA1PRNG, so users of {@code SecureRandom} should not assume
* the same seed will always produce the same value. This test
* is not a guarantee of future compatibility.
+ *
+ * In fact, this test only works with the Harmony "Crypto" provider.
*/
public void testAlwaysSameValueWithSameSeed() throws Exception {
byte[] seed1 = { 'a', 'b', 'c' };
- SecureRandom sr1 = SecureRandom.getInstance("SHA1PRNG");
+ SecureRandom sr1 = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr1.setSeed(seed1);
// This long value has no special meaning and may change in the future.
diff --git a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java
index 2d78238..15b175c 100644
--- a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java
+++ b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/NativeCryptoTest.java
@@ -577,7 +577,7 @@
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<TestSSLHandshakeCallbacks> future = executor.submit(
new Callable<TestSSLHandshakeCallbacks>() {
- public TestSSLHandshakeCallbacks call() throws Exception {
+ @Override public TestSSLHandshakeCallbacks call() throws Exception {
Socket socket = (client
? new Socket(listener.getInetAddress(),
listener.getLocalPort())
@@ -1658,6 +1658,12 @@
assertNotNull(b);
int session2 = NativeCrypto.d2i_SSL_SESSION(b);
assertTrue(session2 != NULL);
+
+ // Make sure d2i_SSL_SESSION retores SSL_SESSION_cipher value http://b/7091840
+ assertTrue(NativeCrypto.SSL_SESSION_cipher(session2) != null);
+ assertEquals(NativeCrypto.SSL_SESSION_cipher(session),
+ NativeCrypto.SSL_SESSION_cipher(session2));
+
NativeCrypto.SSL_SESSION_free(session2);
super.afterHandshake(session, s, c, sock, fd, callback);
}
@@ -1679,7 +1685,7 @@
assertEquals(NULL, NativeCrypto.d2i_SSL_SESSION(new byte[0]));
assertEquals(NULL, NativeCrypto.d2i_SSL_SESSION(new byte[1]));
- // positively testing by test_i2d_SSL_SESSION
+ // positive testing by test_i2d_SSL_SESSION
}
public void test_X509_NAME_hashes() {
@@ -1701,4 +1707,25 @@
// Success
}
}
+
+ public void test_RAND_bytes_Success() throws Exception {
+ byte[] output = new byte[32];
+ NativeCrypto.RAND_bytes(output);
+
+ boolean isZero = true;
+ for (int i = 0; i < output.length; i++) {
+ isZero &= (output[i] == 0);
+ }
+
+ assertFalse("Random output was zero. This is a very low probability event "
+ + "and probably indicates an error.", isZero);
+ }
+
+ public void test_RAND_bytes_Null_Failure() throws Exception {
+ byte[] output = null;
+ try {
+ NativeCrypto.RAND_bytes(output);
+ fail("Should be an error on null buffer input");
+ } catch (RuntimeException success) { }
+ }
}
diff --git a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStoreTest.java b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStoreTest.java
index 6d0f50c..52880df 100644
--- a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStoreTest.java
+++ b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStoreTest.java
@@ -22,11 +22,13 @@
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
+import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
@@ -411,6 +413,15 @@
assertAliases(alias1, alias2);
assertEquals(getChain()[2], store.findIssuer(getChain()[1]));
assertEquals(getChain()[1], store.findIssuer(getChain()[0]));
+
+ X509Certificate[] expected = getChain();
+ List<X509Certificate> actualList = store.getCertificateChain(expected[0]);
+
+ assertEquals("Generated CA list should be same length", expected.length, actualList.size());
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals("Chain value should be the same for position " + i, expected[i],
+ actualList.get(i));
+ }
resetStore();
}
diff --git a/luni/src/test/java/tests/api/java/util/SimpleTimeZoneTest.java b/luni/src/test/java/tests/api/java/util/SimpleTimeZoneTest.java
index e27ec0d..618cbe4 100644
--- a/luni/src/test/java/tests/api/java/util/SimpleTimeZoneTest.java
+++ b/luni/src/test/java/tests/api/java/util/SimpleTimeZoneTest.java
@@ -691,6 +691,7 @@
* java.util.SimpleTimeZone#setStartRule(int, int, int, int, boolean)
*/
public void test_setStartRuleIIIIZ() {
+ TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
// Test for method void java.util.SimpleTimeZone.setStartRule(int, int,
// int, int, boolean)
SimpleTimeZone st = new SimpleTimeZone(TimeZone.getTimeZone("EST").getRawOffset(), "EST");
diff --git a/luni/src/test/java/tests/api/javax/net/ServerSocketFactoryTest.java b/luni/src/test/java/tests/api/javax/net/ServerSocketFactoryTest.java
index 053200f..34d7aed 100644
--- a/luni/src/test/java/tests/api/javax/net/ServerSocketFactoryTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ServerSocketFactoryTest.java
@@ -30,158 +30,77 @@
import junit.framework.TestCase;
-import tests.support.Support_PortManager;
-
-
-/**
- * Tests for <code>ServerSocketFactory</code> class constructors and methods.
- */
public class ServerSocketFactoryTest extends TestCase {
- /**
- * javax.net.SocketFactory#SocketFactory()
- */
public void test_Constructor() {
- try {
- ServerSocketFactory sf = new MyServerSocketFactory();
- } catch (Exception e) {
- fail("Unexpected exception " + e.toString());
- }
+ ServerSocketFactory sf = new MyServerSocketFactory();
}
- /**
- * javax.net.ServerSocketFactory#createServerSocket()
- */
- public final void test_createServerSocket_01() {
+ public final void test_createServerSocket() throws Exception {
ServerSocketFactory sf = ServerSocketFactory.getDefault();
- try {
- ServerSocket ss = sf.createServerSocket();
- assertNotNull(ss);
- } catch (SocketException e) {
- } catch (Exception e) {
- fail(e.toString());
- }
+ ServerSocket ss = sf.createServerSocket();
+ assertNotNull(ss);
+ ss.close();
}
- /**
- * javax.net.ServerSocketFactory#createServerSocket(int port)
- */
- public final void test_createServerSocket_02() {
+ public final void test_createServerSocket_I() throws Exception {
ServerSocketFactory sf = ServerSocketFactory.getDefault();
- int portNumber = Support_PortManager.getNextPort();
+ ServerSocket ss = sf.createServerSocket(0);
+ assertNotNull(ss);
try {
- ServerSocket ss = sf.createServerSocket(portNumber);
- assertNotNull(ss);
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
-
- try {
- sf.createServerSocket(portNumber);
+ sf.createServerSocket(ss.getLocalPort());
fail("IOException wasn't thrown");
- } catch (IOException ioe) {
- //expected
- } catch (Exception ex) {
- fail(ex + " was thrown instead of IOException");
+ } catch (IOException expected) {
}
+ ss.close();
+
try {
sf.createServerSocket(-1);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException ioe) {
- //expected
- } catch (Exception ex) {
- fail(ex + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
- /**
- * javax.net.ServerSocketFactory#createServerSocket(int port, int backlog)
- */
- public final void test_createServerSocket_03() {
+ public final void test_createServerSocket_II() throws Exception {
ServerSocketFactory sf = ServerSocketFactory.getDefault();
- int portNumber = Support_PortManager.getNextPort();
+ ServerSocket ss = sf.createServerSocket(0, 0);
+ assertNotNull(ss);
try {
- ServerSocket ss = sf.createServerSocket(portNumber, 0);
- assertNotNull(ss);
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
-
- try {
- sf.createServerSocket(portNumber, 0);
+ sf.createServerSocket(ss.getLocalPort(), 0);
fail("IOException wasn't thrown");
- } catch (IOException ioe) {
- //expected
- } catch (Exception ex) {
- fail(ex + " was thrown instead of IOException");
+ } catch (IOException expected) {
}
+ ss.close();
+
try {
sf.createServerSocket(65536, 0);
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException ioe) {
- //expected
- } catch (Exception ex) {
- fail(ex + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
- /**
- * javax.net.ServerSocketFactory#createServerSocket(int port, int backlog, InetAddress ifAddress)
- */
- public final void test_createServerSocket_04() {
+ public final void test_createServerSocket_IIInetAddress() throws Exception {
ServerSocketFactory sf = ServerSocketFactory.getDefault();
- int portNumber = Support_PortManager.getNextPort();
+
+ ServerSocket ss = sf.createServerSocket(0, 0, InetAddress.getLocalHost());
+ assertNotNull(ss);
try {
- ServerSocket ss = sf.createServerSocket(portNumber, 0, InetAddress.getLocalHost());
- assertNotNull(ss);
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
-
- try {
- sf.createServerSocket(portNumber, 0, InetAddress.getLocalHost());
+ sf.createServerSocket(ss.getLocalPort(), 0, InetAddress.getLocalHost());
fail("IOException wasn't thrown");
- } catch (IOException ioe) {
- //expected
- } catch (Exception ex) {
- fail(ex + " was thrown instead of IOException");
+ } catch (IOException expected) {
}
+ ss.close();
+
try {
sf.createServerSocket(Integer.MAX_VALUE, 0, InetAddress.getLocalHost());
fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException ioe) {
- //expected
- } catch (Exception ex) {
- fail(ex + " was thrown instead of IllegalArgumentException");
- }
- }
-
- /**
- * javax.net.ServerSocketFactory#getDefault()
- */
- public final void test_getDefault() {
- ServerSocketFactory sf = ServerSocketFactory.getDefault();
- ServerSocket s;
- try {
- s = sf.createServerSocket(0);
- s.close();
- } catch (IOException e) {
- }
- try {
- s = sf.createServerSocket(0, 50);
- s.close();
- } catch (IOException e) {
- }
- try {
- s = sf.createServerSocket(0, 50, InetAddress.getLocalHost());
- s.close();
- } catch (IOException e) {
+ } catch (IllegalArgumentException expected) {
}
}
}
diff --git a/luni/src/test/java/tests/api/javax/net/SocketFactoryTest.java b/luni/src/test/java/tests/api/javax/net/SocketFactoryTest.java
index 2250602..e939a9b 100644
--- a/luni/src/test/java/tests/api/javax/net/SocketFactoryTest.java
+++ b/luni/src/test/java/tests/api/javax/net/SocketFactoryTest.java
@@ -33,200 +33,135 @@
import junit.framework.TestCase;
-import tests.support.Support_PortManager;
-
-
-/**
- * Tests for <code>SocketFactory</code> class methods.
- */
public class SocketFactoryTest extends TestCase {
- /**
- * javax.net.SocketFactory#SocketFactory()
- */
- public void test_Constructor() {
- try {
- MySocketFactory sf = new MySocketFactory();
- } catch (Exception e) {
- fail("Unexpected exception " + e.toString());
- }
+ public void test_Constructor() throws Exception {
+ new MySocketFactory();
}
- /**
- * javax.net.SocketFactory#createSocket()
- */
- public final void test_createSocket_01() {
+ public final void test_createSocket() throws Exception {
SocketFactory sf = SocketFactory.getDefault();
- try {
- Socket s = sf.createSocket();
- assertNotNull(s);
- assertEquals(-1, s.getLocalPort());
- assertEquals(0, s.getPort());
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
+ Socket s = sf.createSocket();
+ assertNotNull(s);
+ assertEquals(-1, s.getLocalPort());
+ assertEquals(0, s.getPort());
MySocketFactory msf = new MySocketFactory();
try {
msf.createSocket();
fail("No expected SocketException");
- } catch (SocketException e) {
- } catch (IOException e) {
- fail(e.toString());
+ } catch (SocketException expected) {
}
}
- /**
- * javax.net.SocketFactory#createSocket(String host, int port)
- */
- public final void test_createSocket_02() {
+ public final void test_createSocket_StringI() throws Exception {
SocketFactory sf = SocketFactory.getDefault();
- int portNumber = Support_PortManager.getNextPort();
- int sport = startServer("Cons String,I");
+ int sport = new ServerSocket(0).getLocalPort();
int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE};
- try {
- Socket s = sf.createSocket(InetAddress.getLocalHost().getHostName(), sport);
- assertNotNull(s);
- assertTrue("Failed to create socket", s.getPort() == sport);
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
+ Socket s = sf.createSocket(InetAddress.getLocalHost().getHostName(), sport);
+ assertNotNull(s);
+ assertTrue("Failed to create socket", s.getPort() == sport);
try {
- Socket s = sf.createSocket("bla-bla", sport);
+ sf.createSocket("bla-bla", sport);
fail("UnknownHostException wasn't thrown");
- } catch (UnknownHostException uhe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of UnknownHostException");
+ } catch (UnknownHostException expected) {
}
for (int i = 0; i < invalidPorts.length; i++) {
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost().getHostName(), invalidPorts[i]);
+ sf.createSocket(InetAddress.getLocalHost().getHostName(), invalidPorts[i]);
fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPorts[i]);
+ } catch (IllegalArgumentException expected) {
}
}
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost().getHostName(), portNumber);
+ sf.createSocket(InetAddress.getLocalHost().getHostName(), s.getLocalPort());
fail("IOException wasn't thrown");
- } catch (IOException ioe) {
- //expected
+ } catch (IOException expected) {
}
SocketFactory f = SocketFactory.getDefault();
try {
- Socket s = f.createSocket("localhost", 8082);
+ f.createSocket(InetAddress.getLocalHost().getHostName(), 8082);
fail("IOException wasn't thrown ...");
- } catch (IOException e) {
+ } catch (IOException expected) {
}
}
- /**
- * javax.net.SocketFactory#createSocket(InetAddress host, int port)
- */
- public final void test_createSocket_03() {
+ public final void test_createSocket_InetAddressI() throws Exception {
SocketFactory sf = SocketFactory.getDefault();
- int portNumber = Support_PortManager.getNextPort();
- int sport = startServer("Cons InetAddress,I");
+ int sport = new ServerSocket(0).getLocalPort();
int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE};
- try {
- Socket s = sf.createSocket(InetAddress.getLocalHost(), sport);
- assertNotNull(s);
- assertTrue("Failed to create socket", s.getPort() == sport);
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
+ Socket s = sf.createSocket(InetAddress.getLocalHost(), sport);
+ assertNotNull(s);
+ assertTrue("Failed to create socket", s.getPort() == sport);
for (int i = 0; i < invalidPorts.length; i++) {
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost(), invalidPorts[i]);
+ sf.createSocket(InetAddress.getLocalHost(), invalidPorts[i]);
fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPorts[i]);
+ } catch (IllegalArgumentException expected) {
}
}
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost(), portNumber);
+ sf.createSocket(InetAddress.getLocalHost(), s.getLocalPort());
fail("IOException wasn't thrown");
- } catch (IOException ioe) {
- //expected
+ } catch (IOException expected) {
}
SocketFactory f = SocketFactory.getDefault();
try {
- Socket s = f.createSocket(InetAddress.getLocalHost(), 8081);
+ f.createSocket(InetAddress.getLocalHost(), 8081);
fail("IOException wasn't thrown ...");
- } catch (IOException e) {
+ } catch (IOException expected) {
}
}
- /**
- * javax.net.SocketFactory#createSocket(InetAddress address, int port,
- * InetAddress localAddress, int localPort)
- */
- public final void test_createSocket_04() {
+ public final void test_createSocket_InetAddressIInetAddressI() throws Exception {
SocketFactory sf = SocketFactory.getDefault();
- int portNumber = Support_PortManager.getNextPort();
- int sport = startServer("Cons InetAddress,I,InetAddress,I");
+ int sport = new ServerSocket(0).getLocalPort();
int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE};
- try {
- Socket s = sf.createSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), portNumber);
- assertNotNull(s);
- assertTrue("1: Failed to create socket", s.getPort() == sport);
- assertTrue("2: Failed to create socket", s.getLocalPort() == portNumber);
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
+ Socket s = sf.createSocket(InetAddress.getLocalHost(), sport,
+ InetAddress.getLocalHost(), 0);
+ assertNotNull(s);
+ assertTrue("1: Failed to create socket", s.getPort() == sport);
+ int portNumber = s.getLocalPort();
for (int i = 0; i < invalidPorts.length; i++) {
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost(), invalidPorts[i],
- InetAddress.getLocalHost(), portNumber);
+ sf.createSocket(InetAddress.getLocalHost(), invalidPorts[i],
+ InetAddress.getLocalHost(), portNumber);
fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPorts[i]);
+ } catch (IllegalArgumentException expected) {
}
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), invalidPorts[i]);
+ sf.createSocket(InetAddress.getLocalHost(), sport,
+ InetAddress.getLocalHost(), invalidPorts[i]);
fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPorts[i]);
+ } catch (IllegalArgumentException expected) {
}
}
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), portNumber);
+ sf.createSocket(InetAddress.getLocalHost(), sport,
+ InetAddress.getLocalHost(), portNumber);
fail("IOException wasn't thrown");
- } catch (IOException ioe) {
- //expected
+ } catch (IOException expected) {
}
SocketFactory f = SocketFactory.getDefault();
try {
- Socket s = f.createSocket(InetAddress.getLocalHost(), 8081, InetAddress.getLocalHost(), 8082);
+ f.createSocket(InetAddress.getLocalHost(), 8081, InetAddress.getLocalHost(), 8082);
fail("IOException wasn't thrown ...");
- } catch (IOException e) {
+ } catch (IOException expected) {
}
}
@@ -234,59 +169,41 @@
* javax.net.SocketFactory#createSocket(String host, int port,
* InetAddress localHost, int localPort)
*/
- public final void test_createSocket_05() {
+ public final void test_createSocket_05() throws Exception {
SocketFactory sf = SocketFactory.getDefault();
- int portNumber = Support_PortManager.getNextPort();
- int sport = startServer("Cons String,I,InetAddress,I");
+ int sport = new ServerSocket(0).getLocalPort();
int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE};
- try {
- Socket s = sf.createSocket(InetAddress.getLocalHost().getHostName(), sport,
- InetAddress.getLocalHost(), portNumber);
- assertNotNull(s);
- assertTrue("1: Failed to create socket", s.getPort() == sport);
- assertTrue("2: Failed to create socket", s.getLocalPort() == portNumber);
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
+ Socket s = sf.createSocket(InetAddress.getLocalHost().getHostName(), sport,
+ InetAddress.getLocalHost(), 0);
+ assertNotNull(s);
+ assertTrue("1: Failed to create socket", s.getPort() == sport);
- portNumber = Support_PortManager.getNextPort();
try {
- Socket s = sf.createSocket("bla-bla", sport, InetAddress.getLocalHost(), portNumber);
+ sf.createSocket("bla-bla", sport, InetAddress.getLocalHost(), 0);
fail("UnknownHostException wasn't thrown");
- } catch (UnknownHostException uhe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of UnknownHostException");
+ } catch (UnknownHostException expected) {
}
for (int i = 0; i < invalidPorts.length; i++) {
- portNumber = Support_PortManager.getNextPort();
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost().getHostName(), invalidPorts[i],
- InetAddress.getLocalHost(), portNumber);
+ sf.createSocket(InetAddress.getLocalHost().getHostName(), invalidPorts[i],
+ InetAddress.getLocalHost(), 0);
fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPorts[i]);
+ } catch (IllegalArgumentException expected) {
}
try {
- Socket s = sf.createSocket(InetAddress.getLocalHost().getHostName(), sport,
- InetAddress.getLocalHost(), invalidPorts[i]);
+ sf.createSocket(InetAddress.getLocalHost().getHostName(), sport,
+ InetAddress.getLocalHost(), invalidPorts[i]);
fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPorts[i]);
+ } catch (IllegalArgumentException expected) {
}
}
- SocketFactory f = SocketFactory.getDefault();
try {
- Socket s = f.createSocket("localhost", 8081, InetAddress.getLocalHost(), 8082);
+ sf.createSocket(InetAddress.getLocalHost().getHostName(), 8081, InetAddress.getLocalHost(), 8082);
fail("IOException wasn't thrown ...");
- } catch (IOException e) {
+ } catch (IOException expected) {
}
}
@@ -297,12 +214,12 @@
SocketFactory sf = SocketFactory.getDefault();
Socket s;
try {
- s = sf.createSocket("localhost", 8082);
+ s = sf.createSocket(InetAddress.getLocalHost().getHostName(), 8082);
s.close();
} catch (IOException e) {
}
try {
- s = sf.createSocket("localhost", 8081, InetAddress.getLocalHost(), 8082);
+ s = sf.createSocket(InetAddress.getLocalHost().getHostName(), 8081, InetAddress.getLocalHost(), 8082);
s.close();
} catch (IOException e) {
}
@@ -317,17 +234,6 @@
} catch (IOException e) {
}
}
-
- protected int startServer(String name) {
- int portNumber = Support_PortManager.getNextPort();
- ServerSocket ss = null;
- try {
- ss = new ServerSocket(portNumber);
- } catch (IOException e) {
- fail(name + ": " + e);
- }
- return ss.getLocalPort();
- }
}
class MySocketFactory extends SocketFactory {
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/HandshakeCompletedEventTest.java b/luni/src/test/java/tests/api/javax/net/ssl/HandshakeCompletedEventTest.java
index 8a02f9c..c075cea 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/HandshakeCompletedEventTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/HandshakeCompletedEventTest.java
@@ -42,7 +42,6 @@
import junit.framework.TestCase;
import libcore.io.Base64;
import org.apache.harmony.xnet.tests.support.mySSLSession;
-import tests.support.Support_PortManager;
/**
* Tests for <code>HandshakeCompletedEvent</code> class constructors and methods.
@@ -50,7 +49,7 @@
*/
public class HandshakeCompletedEventTest extends TestCase {
- String certificate = "-----BEGIN CERTIFICATE-----\n"
+ private String certificate = "-----BEGIN CERTIFICATE-----\n"
+ "MIICZTCCAdICBQL3AAC2MA0GCSqGSIb3DQEBAgUAMF8xCzAJBgNVBAYTAlVTMSAw\n"
+ "HgYDVQQKExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEuMCwGA1UECxMlU2VjdXJl\n"
+ "IFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05NzAyMjAwMDAwMDBa\n"
@@ -200,11 +199,10 @@
// TrustManager
- SSLSocket socket;
- SSLSocket serverSocket;
- MyHandshakeListener listener;
- int port = Support_PortManager.getNextPort();
- String host = "localhost";
+ private SSLSocket socket;
+ private SSLServerSocket serverSocket;
+ private MyHandshakeListener listener;
+ private String host = "localhost";
private String PASSWORD = "android";
@@ -398,35 +396,34 @@
private boolean provideKeys;
- public TestServer(boolean provideKeys, int clientAuth, String keys) {
+ public TestServer(boolean provideKeys, int clientAuth, String keys) throws Exception {
this.keys = keys;
this.clientAuth = clientAuth;
this.provideKeys = provideKeys;
trustManager = new TestTrustManager();
+
+ KeyManager[] keyManagers = provideKeys ? getKeyManagers(keys) : null;
+ TrustManager[] trustManagers = new TrustManager[] { trustManager };
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(keyManagers, trustManagers, null);
+
+ serverSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket();
+
+ if (clientAuth == CLIENT_AUTH_WANTED) {
+ serverSocket.setWantClientAuth(true);
+ } else if (clientAuth == CLIENT_AUTH_NEEDED) {
+ serverSocket.setNeedClientAuth(true);
+ } else {
+ serverSocket.setWantClientAuth(false);
+ }
+
+ serverSocket.bind(new InetSocketAddress(0));
}
public void run() {
try {
- KeyManager[] keyManagers = provideKeys ? getKeyManagers(keys) : null;
- TrustManager[] trustManagers = new TrustManager[] { trustManager };
-
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(keyManagers, trustManagers, null);
-
- SSLServerSocket serverSocket = (SSLServerSocket)
- sslContext.getServerSocketFactory().createServerSocket();
-
- if (clientAuth == CLIENT_AUTH_WANTED) {
- serverSocket.setWantClientAuth(true);
- } else if (clientAuth == CLIENT_AUTH_NEEDED) {
- serverSocket.setNeedClientAuth(true);
- } else {
- serverSocket.setWantClientAuth(false);
- }
-
- serverSocket.bind(new InetSocketAddress(port));
-
SSLSocket clientSocket = (SSLSocket)serverSocket.accept();
InputStream istream = clientSocket.getInputStream();
@@ -497,7 +494,7 @@
SSLSocket socket = (SSLSocket)sslContext.getSocketFactory().createSocket();
- socket.connect(new InetSocketAddress(port));
+ socket.connect(serverSocket.getLocalSocketAddress());
socket.addHandshakeCompletedListener(listener);
socket.startHandshake();
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java b/luni/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
index cc96782..5086f65 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
@@ -20,8 +20,6 @@
import libcore.io.Base64;
-import tests.support.Support_PortManager;
-
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -120,127 +118,78 @@
/**
* javax.net.ssl.SSLServerSocket#SSLServerSocket()
*/
- public void testConstructor_01() {
- try {
- SSLServerSocket ssl = new mySSLServerSocket();
- } catch (Exception ex) {
- fail("Unexpected exception was thrown " + ex);
- }
+ public void testConstructor() throws Exception {
+ SSLServerSocket ssl = new mySSLServerSocket();
}
/**
* javax.net.ssl.SSLServerSocket#SSLServerSocket(int port)
*/
- public void testConstructor_02() {
- SSLServerSocket ssl;
- int portNumber = Support_PortManager.getNextPort();
+ public void testConstructor_I() throws Exception {
int[] port_invalid = {-1, 65536, Integer.MIN_VALUE, Integer.MAX_VALUE};
- try {
- ssl = new mySSLServerSocket(portNumber);
- assertEquals(portNumber, ssl.getLocalPort());
- } catch (Exception ex) {
- fail("Unexpected exception was thrown " + ex);
- }
+ SSLServerSocket ssl = new mySSLServerSocket(0);
for (int i = 0; i < port_invalid.length; i++) {
try {
- ssl = new mySSLServerSocket(port_invalid[i]);
+ new mySSLServerSocket(port_invalid[i]);
fail("IllegalArgumentException should be thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
try {
- ssl = new mySSLServerSocket(portNumber);
- new mySSLServerSocket(portNumber);
+ new mySSLServerSocket(ssl.getLocalPort());
fail("IOException Expected when opening an already opened port");
- } catch (IOException ioe) {
- // expected
- } catch (Exception ex) {
- fail("Unexpected exception was thrown " + ex);
+ } catch (IOException expected) {
}
}
/**
* javax.net.ssl.SSLServerSocket#SSLServerSocket(int port, int backlog)
*/
- public void testConstructor_03() {
- mySSLServerSocket ssl;
- int portNumber = Support_PortManager.getNextPort();
+ public void testConstructor_II() throws Exception {
+ mySSLServerSocket ssl = new mySSLServerSocket(0, 1);
int[] port_invalid = {-1, Integer.MIN_VALUE, Integer.MAX_VALUE};
- try {
- ssl = new mySSLServerSocket(portNumber, 1);
- assertEquals(portNumber, ssl.getLocalPort());
- } catch (Exception ex) {
- fail("Unexpected exception was thrown");
- }
-
for (int i = 0; i < port_invalid.length; i++) {
try {
- ssl = new mySSLServerSocket(port_invalid[i], 1);
+ new mySSLServerSocket(port_invalid[i], 1);
fail("IllegalArgumentException should be thrown");
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
- portNumber = Support_PortManager.getNextPort();
try {
- ssl = new mySSLServerSocket(portNumber, 1);
- new mySSLServerSocket(portNumber, 1);
+ new mySSLServerSocket(ssl.getLocalPort(), 1);
fail("IOException should be thrown");
- } catch (IOException ioe) {
+ } catch (IOException expected) {
}
}
/**
* javax.net.ssl.SSLServerSocket#SSLServerSocket(int port, int backlog, InetAddress address)
*/
- public void testConstructor_04() {
- mySSLServerSocket ssl;
- InetAddress ia = null;
- int portNumber = Support_PortManager.getNextPort();
+ public void testConstructor_IIInetAddress() throws Exception {
+ // A null InetAddress is okay.
+ new mySSLServerSocket(0, 0, null);
+
int[] port_invalid = {-1, 65536, Integer.MIN_VALUE, Integer.MAX_VALUE};
- try {
- ssl = new mySSLServerSocket(portNumber, 0, ia);
- assertEquals(portNumber, ssl.getLocalPort());
- } catch (Exception ex) {
- fail("Unexpected exception was thrown");
- }
-
- portNumber = Support_PortManager.getNextPort();
- try {
- ssl = new mySSLServerSocket(portNumber, 0, InetAddress.getLocalHost());
- assertEquals(portNumber, ssl.getLocalPort());
- } catch (Exception ex) {
- fail("Unexpected exception was thrown");
- }
+ mySSLServerSocket ssl = new mySSLServerSocket(0, 0, InetAddress.getLocalHost());
for (int i = 0; i < port_invalid.length; i++) {
try {
- ssl = new mySSLServerSocket(port_invalid[i], 1, InetAddress.getLocalHost());
+ new mySSLServerSocket(port_invalid[i], 1, InetAddress.getLocalHost());
fail("IllegalArgumentException should be thrown");
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
}
}
- portNumber = Support_PortManager.getNextPort();
try {
- ssl = new mySSLServerSocket(portNumber, 0, InetAddress.getLocalHost());
- new mySSLServerSocket(portNumber, 0, InetAddress.getLocalHost());
- fail("IOException should be thrown for");
- } catch (IOException ioe) {
+ new mySSLServerSocket(ssl.getLocalPort(), 0, InetAddress.getLocalHost());
+ fail("IOException should be thrown for");
+ } catch (IOException expected) {
}
}
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java b/luni/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
index ec23cae..5084422 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
@@ -39,12 +39,7 @@
import libcore.io.Base64;
import tests.api.javax.net.ssl.HandshakeCompletedEventTest.MyHandshakeListener;
import tests.api.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager;
-import tests.support.Support_PortManager;
-/**
- * Tests for SSLSession class
- *
- */
public class SSLSessionTest extends TestCase {
// set to true if on Android, false if on RI
@@ -57,7 +52,7 @@
public void test_getPeerHost() throws Exception {
SSLSession s = clientSession;
assertEquals(InetAddress.getLocalHost().getHostName(), s.getPeerHost());
- assertEquals(port, s.getPeerPort());
+ assertEquals(serverSocket.getLocalPort(), s.getPeerPort());
}
/**
@@ -258,12 +253,10 @@
TestClient client;
@Override
- protected void setUp() {
- port = Support_PortManager.getNextPort();
+ protected void setUp() throws Exception {
String serverKeys = (useBKS ? SERVER_KEYS_BKS : SERVER_KEYS_JKS);
String clientKeys = (useBKS ? CLIENT_KEYS_BKS : CLIENT_KEYS_JKS);
- server = new TestServer(true,
- TestServer.CLIENT_AUTH_WANTED, serverKeys);
+ server = new TestServer(true, TestServer.CLIENT_AUTH_WANTED, serverKeys);
client = new TestClient(true, clientKeys);
serverThread = new Thread(server);
@@ -453,8 +446,7 @@
+ "NMGpCX6qmjbkJQLVK/Yfo1ePaUexPSOX0G9m8+DoV3iyNw6at01NRw==";
- int port;
- SSLSocket serverSocket;
+ SSLServerSocket serverSocket;
MyHandshakeListener listener;
String host = "localhost";
boolean notFinished = true;
@@ -489,36 +481,35 @@
private KeyStore store;
- public TestServer(boolean provideKeys, int clientAuth, String keys) {
+ public TestServer(boolean provideKeys, int clientAuth, String keys) throws Exception {
this.keys = keys;
this.clientAuth = clientAuth;
this.provideKeys = provideKeys;
trustManager = new TestTrustManager();
+
+ store = provideKeys ? getKeyStore(keys) : null;
+ KeyManager[] keyManagers = store != null ? getKeyManagers(store) : null;
+ TrustManager[] trustManagers = new TrustManager[] { trustManager };
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(keyManagers, trustManagers, null);
+
+ serverSocket = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket();
+
+ if (clientAuth == CLIENT_AUTH_WANTED) {
+ serverSocket.setWantClientAuth(true);
+ } else if (clientAuth == CLIENT_AUTH_NEEDED) {
+ serverSocket.setNeedClientAuth(true);
+ } else {
+ serverSocket.setWantClientAuth(false);
+ }
+
+ serverSocket.bind(null);
}
public void run() {
try {
- store = provideKeys ? getKeyStore(keys) : null;
- KeyManager[] keyManagers = store != null ? getKeyManagers(store) : null;
- TrustManager[] trustManagers = new TrustManager[] { trustManager };
-
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(keyManagers, trustManagers, null);
-
- SSLServerSocket serverSocket = (SSLServerSocket)sslContext
- .getServerSocketFactory().createServerSocket();
-
- if (clientAuth == CLIENT_AUTH_WANTED) {
- serverSocket.setWantClientAuth(true);
- } else if (clientAuth == CLIENT_AUTH_NEEDED) {
- serverSocket.setNeedClientAuth(true);
- } else {
- serverSocket.setWantClientAuth(false);
- }
-
- serverSocket.bind(new InetSocketAddress(port));
-
SSLSocket clientSocket = (SSLSocket)serverSocket.accept();
InputStream istream = clientSocket.getInputStream();
@@ -589,7 +580,7 @@
SSLSocket socket = (SSLSocket)clientSslContext.getSocketFactory().createSocket();
- socket.connect(new InetSocketAddress(port));
+ socket.connect(serverSocket.getLocalSocketAddress());
OutputStream ostream = socket.getOutputStream();
ostream.write(testData.getBytes());
ostream.flush();
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketFactoryTest.java b/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketFactoryTest.java
index 02abcc2..0d91116 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketFactoryTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketFactoryTest.java
@@ -25,16 +25,13 @@
import junit.framework.TestCase;
-import tests.support.Support_PortManager;
-
public class SSLSocketFactoryTest extends TestCase {
private ServerSocket ss;
protected int startServer(String name) {
- int portNumber = Support_PortManager.getNextPort();
try {
- ss = new ServerSocket(portNumber);
+ ss = new ServerSocket(0);
} catch (IOException e) {
fail(name + ": " + e);
}
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
index ab60f72..b4cbde2 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
@@ -38,7 +38,6 @@
import junit.framework.TestCase;
import libcore.io.Base64;
import tests.api.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager;
-import tests.support.Support_PortManager;
import libcore.java.security.StandardNames;
public class SSLSocketTest extends TestCase {
@@ -51,7 +50,7 @@
/**
* javax.net.ssl.SSLSocket#SSLSocket()
*/
- public void testConstructor_01() throws Exception {
+ public void testConstructor() throws Exception {
SSLSocket ssl = getSSLSocket();
assertNotNull(ssl);
ssl.close();
@@ -60,7 +59,7 @@
/**
* javax.net.ssl.SSLSocket#SSLSocket(InetAddress address, int port)
*/
- public void testConstructor_02() throws UnknownHostException, IOException {
+ public void testConstructor_InetAddressI() throws Exception {
int sport = startServer("Cons InetAddress,I");
int[] invalidPort = {-1, Integer.MIN_VALUE, 65536, Integer.MAX_VALUE};
@@ -88,15 +87,13 @@
* javax.net.ssl.SSLSocket#SSLSocket(InetAddress address, int port,
* InetAddress clientAddress, int clientPort)
*/
- public void testConstructor_03() throws UnknownHostException, IOException {
+ public void testConstructor_InetAddressIInetAddressI() throws Exception {
int sport = startServer("Cons InetAddress,I,InetAddress,I");
- int portNumber = Support_PortManager.getNextPort();
SSLSocket ssl = getSSLSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), portNumber);
+ InetAddress.getLocalHost(), 0);
assertNotNull(ssl);
assertEquals(sport, ssl.getPort());
- assertEquals(portNumber, ssl.getLocalPort());
ssl.close();
try {
@@ -137,7 +134,7 @@
/**
* javax.net.ssl.SSLSocket#SSLSocket(String host, int port)
*/
- public void testConstructor_04() throws UnknownHostException, IOException {
+ public void testConstructor_StringI() throws Exception {
int sport = startServer("Cons String,I");
int[] invalidPort = {-1, Integer.MIN_VALUE, 65536, Integer.MAX_VALUE};
@@ -171,28 +168,25 @@
* javax.net.ssl.SSLSocket#SSLSocket(String host, int port, InetAddress clientAddress,
* int clientPort)
*/
- public void testConstructor_05() throws UnknownHostException, IOException {
+ public void testConstructor_StringIInetAddressI() throws Exception {
int sport = startServer("Cons String,I,InetAddress,I");
- int portNumber = Support_PortManager.getNextPort();
int[] invalidPort = {-1, Integer.MIN_VALUE, 65536, Integer.MAX_VALUE};
SSLSocket ssl = getSSLSocket(InetAddress.getLocalHost().getHostName(), sport,
- InetAddress.getLocalHost(), portNumber);
+ InetAddress.getLocalHost(), 0);
assertNotNull(ssl);
assertEquals(sport, ssl.getPort());
- assertEquals(portNumber, ssl.getLocalPort());
try {
- getSSLSocket("localhost", 8081, InetAddress.getLocalHost(), 8082);
+ getSSLSocket(InetAddress.getLocalHost().getHostName(), 8081, InetAddress.getLocalHost(), 8082);
fail();
} catch (IOException expected) {
}
for (int i = 0; i < invalidPort.length; i++) {
- portNumber = Support_PortManager.getNextPort();
try {
getSSLSocket(InetAddress.getLocalHost().getHostName(), invalidPort[i],
- InetAddress.getLocalHost(), portNumber);
+ InetAddress.getLocalHost(), 0);
fail();
} catch (IllegalArgumentException expected) {
}
@@ -204,9 +198,8 @@
}
}
- portNumber = Support_PortManager.getNextPort();
try {
- getSSLSocket("bla-bla", sport, InetAddress.getLocalHost(), portNumber);
+ getSSLSocket("bla-bla", sport, InetAddress.getLocalHost(), 0);
fail();
} catch (UnknownHostException expected) {
}
@@ -422,12 +415,10 @@
ssl.close();
}
- boolean useBKS = !StandardNames.IS_RI;
+ private boolean useBKS = !StandardNames.IS_RI;
private String PASSWORD = "android";
- private int port = Support_PortManager.getNextPort();
-
private boolean serverReady = false;
/**
@@ -551,7 +542,7 @@
SSLServerSocket serverSocket = (SSLServerSocket)
sslContext.getServerSocketFactory().createServerSocket();
try {
- serverSocket.bind(new InetSocketAddress(port));
+ serverSocket.bind(new InetSocketAddress(0));
sport = serverSocket.getLocalPort();
serverReady = true;
diff --git a/luni/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java b/luni/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java
index 286d4ab..bdc580d 100644
--- a/luni/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java
+++ b/luni/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java
@@ -187,11 +187,10 @@
if (! completed) {
try {
wait(maxWaitMillis);
- } catch (InterruptedException ex) {
- // Ignore it.
+ } catch (InterruptedException ignored) {
}
if (! completed) {
- Assert.fail("parker hanging");
+ Assert.fail("parker hung for more than " + maxWaitMillis + " ms");
}
}
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index e8b29e4..eb53ba5 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -274,6 +274,12 @@
provide("Signature", "SHA512WITHECDSA");
}
+ // Documented as Standard Names, but do not exit in RI 6
+ if (IS_RI) {
+ unprovide("SSLContext", "TLSv1.1");
+ unprovide("SSLContext", "TLSv1.2");
+ }
+
// Fixups for dalvik
if (!IS_RI) {
@@ -328,6 +334,11 @@
unprovide("Signature", "SHA512WithRSA");
provide("Signature", "SHA512WithRSAEncryption");
+ // Added to support Android KeyStore operations
+ provide("Signature", "NONEwithRSA");
+ provide("Cipher", "RSA/ECB/NOPADDING");
+ provide("Cipher", "RSA/ECB/PKCS1PADDING");
+
// different names: JSSE Reference Guide says PKIX aka X509
unprovide("TrustManagerFactory", "PKIX");
provide("TrustManagerFactory", "X509");
diff --git a/support/src/test/java/tests/support/Support_TestWebServer.java b/support/src/test/java/tests/support/Support_TestWebServer.java
index 4d6b0d1..5f3cd85 100644
--- a/support/src/test/java/tests/support/Support_TestWebServer.java
+++ b/support/src/test/java/tests/support/Support_TestWebServer.java
@@ -94,51 +94,21 @@
}
/**
- * Initialize a new server with default port and timeout.
- * @param log Set true if you want trace output
- */
- public int initServer(boolean log) throws Exception {
- return initServer(0, DEFAULT_TIMEOUT, log);
- }
-
- /**
- * Initialize a new server with default timeout.
- * @param port Sets the server to listen on this port, or 0 to let the OS choose.
- * Hard-coding ports is evil, so always pass 0.
- * @param log Set true if you want trace output
- */
- public int initServer(int port, boolean log) throws Exception {
- return initServer(port, DEFAULT_TIMEOUT, log);
- }
-
- /**
- * Initialize a new server with default timeout and disabled log.
- * @param port Sets the server to listen on this port, or 0 to let the OS choose.
- * Hard-coding ports is evil, so always pass 0.
* @param servePath the path to the dynamic web test data
* @param contentType the type of the dynamic web test data
*/
- public int initServer(int port, String servePath, String contentType)
- throws Exception {
+ public int initServer(String servePath, String contentType) throws Exception {
Support_TestWebData.initDynamicTestWebData(servePath, contentType);
- return initServer(port, DEFAULT_TIMEOUT, false);
+ return initServer();
}
- /**
- * Initialize a new server with default port and timeout.
- * @param port Sets the server to listen on this port, or 0 to let the OS choose.
- * Hard-coding ports is evil, so always pass 0.
- * @param timeout Indicates the period of time to wait until a socket is
- * closed
- * @param log Set true if you want trace output
- */
- public int initServer(int port, int timeout, boolean log) throws Exception {
- mTimeout = timeout;
- mLog = log;
+ public int initServer() throws Exception {
+ mTimeout = DEFAULT_TIMEOUT;
+ mLog = false;
keepAlive = true;
if (acceptT == null) {
acceptT = new AcceptThread();
- mPort = acceptT.init(port);
+ mPort = acceptT.init();
acceptT.start();
}
return mPort;
@@ -253,8 +223,8 @@
* @param port the port to use, or 0 to let the OS choose.
* Hard-coding ports is evil, so always pass 0!
*/
- public int init(int port) throws IOException {
- ss = new ServerSocket(port);
+ public int init() throws IOException {
+ ss = new ServerSocket(0);
ss.setSoTimeout(5000);
ss.setReuseAddress(true);
return ss.getLocalPort();
diff --git a/xml/src/main/java/org/kxml2/ThirdPartyProject.prop b/xml/src/main/java/org/kxml2/ThirdPartyProject.prop
deleted file mode 100644
index aabb1c7..0000000
--- a/xml/src/main/java/org/kxml2/ThirdPartyProject.prop
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2010 Google Inc. All Rights Reserved.
-#Fri Jul 16 10:03:09 PDT 2010
-currentVersion=2.3.0
-version=Unknown
-isNative=false
-name=kxml
-keywords=kxml
-onDevice=true
-homepage=http\://kxml.sourceforge.net/
diff --git a/xml/src/main/java/org/kxml2/kdom/Document.java b/xml/src/main/java/org/kxml2/kdom/Document.java
deleted file mode 100644
index 9123e62..0000000
--- a/xml/src/main/java/org/kxml2/kdom/Document.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE. */
-
-
-package org.kxml2.kdom;
-
-import java.io.*;
-
-import org.xmlpull.v1.*;
-/** The document consists of some legacy events and a single root
- element. This class basically adds some consistency checks to
- Node. */
-
-public class Document extends Node {
-
- protected int rootIndex = -1;
- String encoding;
- Boolean standalone;
-
- /** returns "#document" */
-
- public String getEncoding () {
- return encoding;
- }
-
- public void setEncoding(String enc) {
- this.encoding = enc;
- }
-
- public void setStandalone (Boolean standalone) {
- this.standalone = standalone;
- }
-
- public Boolean getStandalone() {
- return standalone;
- }
-
-
- public String getName() {
- return "#document";
- }
-
- /** Adds a child at the given index position. Throws
- an exception when a second root element is added */
-
- public void addChild(int index, int type, Object child) {
- if (type == ELEMENT) {
- // if (rootIndex != -1)
- // throw new RuntimeException("Only one document root element allowed");
-
- rootIndex = index;
- }
- else if (rootIndex >= index)
- rootIndex++;
-
- super.addChild(index, type, child);
- }
-
- /** reads the document and checks if the last event
- is END_DOCUMENT. If not, an exception is thrown.
- The end event is consumed. For parsing partial
- XML structures, consider using Node.parse (). */
-
- public void parse(XmlPullParser parser)
- throws IOException, XmlPullParserException {
-
- parser.require(XmlPullParser.START_DOCUMENT, null, null);
- parser.nextToken ();
-
- encoding = parser.getInputEncoding();
- standalone = (Boolean)parser.getProperty ("http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone");
-
- super.parse(parser);
-
- if (parser.getEventType() != XmlPullParser.END_DOCUMENT)
- throw new RuntimeException("Document end expected!");
-
- }
-
- public void removeChild(int index) {
- if (index == rootIndex)
- rootIndex = -1;
- else if (index < rootIndex)
- rootIndex--;
-
- super.removeChild(index);
- }
-
- /** returns the root element of this document. */
-
- public Element getRootElement() {
- if (rootIndex == -1)
- throw new RuntimeException("Document has no root element!");
-
- return (Element) getChild(rootIndex);
- }
-
-
- /** Writes this node to the given XmlWriter. For node and document,
- this method is identical to writeChildren, except that the
- stream is flushed automatically. */
-
- public void write(XmlSerializer writer)
- throws IOException {
-
- writer.startDocument(encoding, standalone);
- writeChildren(writer);
- writer.endDocument();
- }
-
-
-}
\ No newline at end of file
diff --git a/xml/src/main/java/org/kxml2/kdom/Element.java b/xml/src/main/java/org/kxml2/kdom/Element.java
deleted file mode 100644
index a9cb426..0000000
--- a/xml/src/main/java/org/kxml2/kdom/Element.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE. */
-
-package org.kxml2.kdom;
-
-import java.io.*;
-import java.util.*;
-
-import org.xmlpull.v1.*;
-
-/**
- * In order to create an element, please use the createElement method
- * instead of invoking the constructor directly. The right place to
- * add user defined initialization code is the init method. */
-
-public class Element extends Node {
-
- protected String namespace;
- protected String name;
- protected Vector attributes;
- protected Node parent;
- protected Vector prefixes;
-
- public Element() {
- }
-
- /**
- * called when all properties are set, but before children
- * are parsed. Please do not use setParent for initialization
- * code any longer. */
-
- public void init() {
- }
-
-
-
-
- /**
- * removes all children and attributes */
-
- public void clear() {
- attributes = null;
- children = null;
- }
-
- /**
- * Forwards creation request to parent if any, otherwise
- * calls super.createElement. */
-
- public Element createElement(
- String namespace,
- String name) {
-
- return (this.parent == null)
- ? super.createElement(namespace, name)
- : this.parent.createElement(namespace, name);
- }
-
- /**
- * Returns the number of attributes of this element. */
-
- public int getAttributeCount() {
- return attributes == null ? 0 : attributes.size();
- }
-
- public String getAttributeNamespace (int index) {
- return ((String []) attributes.elementAt (index)) [0];
- }
-
-/* public String getAttributePrefix (int index) {
- return ((String []) attributes.elementAt (index)) [1];
- }*/
-
- public String getAttributeName (int index) {
- return ((String []) attributes.elementAt (index)) [1];
- }
-
-
- public String getAttributeValue (int index) {
- return ((String []) attributes.elementAt (index)) [2];
- }
-
-
- public String getAttributeValue (String namespace, String name) {
- for (int i = 0; i < getAttributeCount (); i++) {
- if (name.equals (getAttributeName (i))
- && (namespace == null || namespace.equals (getAttributeNamespace(i)))) {
- return getAttributeValue (i);
- }
- }
- return null;
- }
-
- /**
- * Returns the root node, determined by ascending to the
- * all parents un of the root element. */
-
- public Node getRoot() {
-
- Element current = this;
-
- while (current.parent != null) {
- if (!(current.parent instanceof Element)) return current.parent;
- current = (Element) current.parent;
- }
-
- return current;
- }
-
- /**
- * returns the (local) name of the element */
-
- public String getName() {
- return name;
- }
-
- /**
- * returns the namespace of the element */
-
- public String getNamespace() {
- return namespace;
- }
-
-
- /**
- * returns the namespace for the given prefix */
-
- public String getNamespaceUri (String prefix) {
- int cnt = getNamespaceCount ();
- for (int i = 0; i < cnt; i++) {
- if (prefix == getNamespacePrefix (i) ||
- (prefix != null && prefix.equals (getNamespacePrefix (i))))
- return getNamespaceUri (i);
- }
- return parent instanceof Element ? ((Element) parent).getNamespaceUri (prefix) : null;
- }
-
-
- /**
- * returns the number of declared namespaces, NOT including
- * parent elements */
-
- public int getNamespaceCount () {
- return (prefixes == null ? 0 : prefixes.size ());
- }
-
-
- public String getNamespacePrefix (int i) {
- return ((String []) prefixes.elementAt (i)) [0];
- }
-
- public String getNamespaceUri (int i) {
- return ((String []) prefixes.elementAt (i)) [1];
- }
-
-
- /**
- * Returns the parent node of this element */
-
- public Node getParent() {
- return parent;
- }
-
- /*
- * Returns the parent element if available, null otherwise
-
- public Element getParentElement() {
- return (parent instanceof Element)
- ? ((Element) parent)
- : null;
- }
-*/
-
- /**
- * Builds the child elements from the given Parser. By overwriting
- * parse, an element can take complete control over parsing its
- * subtree. */
-
- public void parse(XmlPullParser parser)
- throws IOException, XmlPullParserException {
-
- for (int i = parser.getNamespaceCount (parser.getDepth () - 1);
- i < parser.getNamespaceCount (parser.getDepth ()); i++) {
- setPrefix (parser.getNamespacePrefix (i), parser.getNamespaceUri(i));
- }
-
-
- for (int i = 0; i < parser.getAttributeCount (); i++)
- setAttribute (parser.getAttributeNamespace (i),
-// parser.getAttributePrefix (i),
- parser.getAttributeName (i),
- parser.getAttributeValue (i));
-
-
- // if (prefixMap == null) throw new RuntimeException ("!!");
-
- init();
-
-
- if (parser.isEmptyElementTag())
- parser.nextToken ();
- else {
- parser.nextToken ();
- super.parse(parser);
-
- if (getChildCount() == 0)
- addChild(IGNORABLE_WHITESPACE, "");
- }
-
- parser.require(
- XmlPullParser.END_TAG,
- getNamespace(),
- getName());
-
- parser.nextToken ();
- }
-
-
- /**
- * Sets the given attribute; a value of null removes the attribute */
-
- public void setAttribute (String namespace, String name, String value) {
- if (attributes == null)
- attributes = new Vector ();
-
- if (namespace == null)
- namespace = "";
-
- for (int i = attributes.size()-1; i >=0; i--){
- String[] attribut = (String[]) attributes.elementAt(i);
- if (attribut[0].equals(namespace) &&
- attribut[1].equals(name)){
-
- if (value == null) {
- attributes.removeElementAt(i);
- }
- else {
- attribut[2] = value;
- }
- return;
- }
- }
-
- attributes.addElement
- (new String [] {namespace, name, value});
- }
-
-
- /**
- * Sets the given prefix; a namespace value of null removess the
- * prefix */
-
- public void setPrefix (String prefix, String namespace) {
- if (prefixes == null) prefixes = new Vector ();
- prefixes.addElement (new String [] {prefix, namespace});
- }
-
-
- /**
- * sets the name of the element */
-
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * sets the namespace of the element. Please note: For no
- * namespace, please use Xml.NO_NAMESPACE, null is not a legal
- * value. Currently, null is converted to Xml.NO_NAMESPACE, but
- * future versions may throw an exception. */
-
- public void setNamespace(String namespace) {
- if (namespace == null)
- throw new NullPointerException ("Use \"\" for empty namespace");
- this.namespace = namespace;
- }
-
- /**
- * Sets the Parent of this element. Automatically called from the
- * add method. Please use with care, you can simply
- * create inconsitencies in the document tree structure using
- * this method! */
-
- protected void setParent(Node parent) {
- this.parent = parent;
- }
-
-
- /**
- * Writes this element and all children to the given XmlWriter. */
-
- public void write(XmlSerializer writer)
- throws IOException {
-
- if (prefixes != null) {
- for (int i = 0; i < prefixes.size(); i++) {
- writer.setPrefix (getNamespacePrefix (i), getNamespaceUri (i));
- }
- }
-
- writer.startTag(
- getNamespace(),
- getName());
-
- int len = getAttributeCount();
-
- for (int i = 0; i < len; i++) {
- writer.attribute(
- getAttributeNamespace(i),
- getAttributeName(i),
- getAttributeValue(i));
- }
-
- writeChildren(writer);
-
- writer.endTag(getNamespace (), getName ());
- }
-}
diff --git a/xml/src/main/java/org/kxml2/kdom/Node.java b/xml/src/main/java/org/kxml2/kdom/Node.java
deleted file mode 100644
index 820390c..0000000
--- a/xml/src/main/java/org/kxml2/kdom/Node.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE. */
-
-package org.kxml2.kdom;
-
-import java.util.*;
-import java.io.*;
-import org.xmlpull.v1.*;
-/** A common base class for Document and Element, also used for
- storing XML fragments. */
-
-public class Node { //implements XmlIO{
-
- public static final int DOCUMENT = 0;
- public static final int ELEMENT = 2;
- public static final int TEXT = 4;
- public static final int CDSECT = 5;
- public static final int ENTITY_REF = 6;
- public static final int IGNORABLE_WHITESPACE = 7;
- public static final int PROCESSING_INSTRUCTION = 8;
- public static final int COMMENT = 9;
- public static final int DOCDECL = 10;
-
- protected Vector children;
- protected StringBuffer types;
-
- /** inserts the given child object of the given type at the
- given index. */
-
- public void addChild(int index, int type, Object child) {
-
- if (child == null)
- throw new NullPointerException();
-
- if (children == null) {
- children = new Vector();
- types = new StringBuffer();
- }
-
- if (type == ELEMENT) {
- if (!(child instanceof Element))
- throw new RuntimeException("Element obj expected)");
-
- ((Element) child).setParent(this);
- }
- else if (!(child instanceof String))
- throw new RuntimeException("String expected");
-
- children.insertElementAt(child, index);
- types.insert(index, (char) type);
- }
-
- /** convenience method for addChild (getChildCount (), child) */
-
- public void addChild(int type, Object child) {
- addChild(getChildCount(), type, child);
- }
-
- /** Builds a default element with the given properties. Elements
- should always be created using this method instead of the
- constructor in order to enable construction of specialized
- subclasses by deriving custom Document classes. Please note:
- For no namespace, please use Xml.NO_NAMESPACE, null is not a
- legal value. Currently, null is converted to Xml.NO_NAMESPACE,
- but future versions may throw an exception. */
-
- public Element createElement(String namespace, String name) {
-
- Element e = new Element();
- e.namespace = namespace == null ? "" : namespace;
- e.name = name;
- return e;
- }
-
- /** Returns the child object at the given index. For child
- elements, an Element object is returned. For all other child
- types, a String is returned. */
-
- public Object getChild(int index) {
- return children.elementAt(index);
- }
-
- /** Returns the number of child objects */
-
- public int getChildCount() {
- return children == null ? 0 : children.size();
- }
-
- /** returns the element at the given index. If the node at the
- given index is a text node, null is returned */
-
- public Element getElement(int index) {
- Object child = getChild(index);
- return (child instanceof Element) ? (Element) child : null;
- }
-
- /** Returns the element with the given namespace and name. If the
- element is not found, or more than one matching elements are
- found, an exception is thrown. */
-
- public Element getElement(String namespace, String name) {
-
- int i = indexOf(namespace, name, 0);
- int j = indexOf(namespace, name, i + 1);
-
- if (i == -1 || j != -1)
- throw new RuntimeException(
- "Element {"
- + namespace
- + "}"
- + name
- + (i == -1 ? " not found in " : " more than once in ")
- + this);
-
- return getElement(i);
- }
-
- /* returns "#document-fragment". For elements, the element name is returned
-
- public String getName() {
- return "#document-fragment";
- }
-
- /** Returns the namespace of the current element. For Node
- and Document, Xml.NO_NAMESPACE is returned.
-
- public String getNamespace() {
- return "";
- }
-
- public int getNamespaceCount () {
- return 0;
- }
-
- /** returns the text content if the element has text-only
- content. Throws an exception for mixed content
-
- public String getText() {
-
- StringBuffer buf = new StringBuffer();
- int len = getChildCount();
-
- for (int i = 0; i < len; i++) {
- if (isText(i))
- buf.append(getText(i));
- else if (getType(i) == ELEMENT)
- throw new RuntimeException("not text-only content!");
- }
-
- return buf.toString();
- }
- */
-
- /** Returns the text node with the given index or null if the node
- with the given index is not a text node. */
-
- public String getText(int index) {
- return (isText(index)) ? (String) getChild(index) : null;
- }
-
- /** Returns the type of the child at the given index. Possible
- types are ELEMENT, TEXT, COMMENT, and PROCESSING_INSTRUCTION */
-
- public int getType(int index) {
- return types.charAt(index);
- }
-
- /** Convenience method for indexOf (getNamespace (), name,
- startIndex).
-
- public int indexOf(String name, int startIndex) {
- return indexOf(getNamespace(), name, startIndex);
- }
- */
-
- /** Performs search for an element with the given namespace and
- name, starting at the given start index. A null namespace
- matches any namespace, please use Xml.NO_NAMESPACE for no
- namespace). returns -1 if no matching element was found. */
-
- public int indexOf(String namespace, String name, int startIndex) {
-
- int len = getChildCount();
-
- for (int i = startIndex; i < len; i++) {
-
- Element child = getElement(i);
-
- if (child != null
- && name.equals(child.getName())
- && (namespace == null || namespace.equals(child.getNamespace())))
- return i;
- }
- return -1;
- }
-
- public boolean isText(int i) {
- int t = getType(i);
- return t == TEXT || t == IGNORABLE_WHITESPACE || t == CDSECT;
- }
-
- /** Recursively builds the child elements from the given parser
- until an end tag or end document is found.
- The end tag is not consumed. */
-
- public void parse(XmlPullParser parser)
- throws IOException, XmlPullParserException {
-
- boolean leave = false;
-
- do {
- int type = parser.getEventType();
-
- // System.out.println(parser.getPositionDescription());
-
- switch (type) {
-
- case XmlPullParser.START_TAG :
- {
- Element child =
- createElement(
- parser.getNamespace(),
- parser.getName());
- // child.setAttributes (event.getAttributes ());
- addChild(ELEMENT, child);
-
- // order is important here since
- // setparent may perform some init code!
-
- child.parse(parser);
- break;
- }
-
- case XmlPullParser.END_DOCUMENT :
- case XmlPullParser.END_TAG :
- leave = true;
- break;
-
- default :
- if (parser.getText() != null)
- addChild(
- type == XmlPullParser.ENTITY_REF ? TEXT : type,
- parser.getText());
- else if (
- type == XmlPullParser.ENTITY_REF
- && parser.getName() != null) {
- addChild(ENTITY_REF, parser.getName());
- }
- parser.nextToken();
- }
- }
- while (!leave);
- }
-
- /** Removes the child object at the given index */
-
- public void removeChild(int idx) {
- children.removeElementAt(idx);
-
- /*** Modification by HHS - start ***/
- // types.deleteCharAt (index);
- /***/
- int n = types.length() - 1;
-
- for (int i = idx; i < n; i++)
- types.setCharAt(i, types.charAt(i + 1));
-
- types.setLength(n);
-
- /*** Modification by HHS - end ***/
- }
-
- /* returns a valid XML representation of this Element including
- attributes and children.
- public String toString() {
- try {
- ByteArrayOutputStream bos =
- new ByteArrayOutputStream();
- XmlWriter xw =
- new XmlWriter(new OutputStreamWriter(bos));
- write(xw);
- xw.close();
- return new String(bos.toByteArray());
- }
- catch (IOException e) {
- throw new RuntimeException(e.toString());
- }
- }
- */
-
- /** Writes this node to the given XmlWriter. For node and document,
- this method is identical to writeChildren, except that the
- stream is flushed automatically. */
-
- public void write(XmlSerializer writer) throws IOException {
- writeChildren(writer);
- writer.flush();
- }
-
- /** Writes the children of this node to the given XmlWriter. */
-
- public void writeChildren(XmlSerializer writer) throws IOException {
- if (children == null)
- return;
-
- int len = children.size();
-
- for (int i = 0; i < len; i++) {
- int type = getType(i);
- Object child = children.elementAt(i);
- switch (type) {
- case ELEMENT :
- ((Element) child).write(writer);
- break;
-
- case TEXT :
- writer.text((String) child);
- break;
-
- case IGNORABLE_WHITESPACE :
- writer.ignorableWhitespace((String) child);
- break;
-
- case CDSECT :
- writer.cdsect((String) child);
- break;
-
- case COMMENT :
- writer.comment((String) child);
- break;
-
- case ENTITY_REF :
- writer.entityRef((String) child);
- break;
-
- case PROCESSING_INSTRUCTION :
- writer.processingInstruction((String) child);
- break;
-
- case DOCDECL :
- writer.docdecl((String) child);
- break;
-
- default :
- throw new RuntimeException("Illegal type: " + type);
- }
- }
- }
-}
diff --git a/xml/src/main/java/org/xml/ThirdPartyProject.prop b/xml/src/main/java/org/xml/ThirdPartyProject.prop
deleted file mode 100644
index b8ac0da..0000000
--- a/xml/src/main/java/org/xml/ThirdPartyProject.prop
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2010 Google Inc. All Rights Reserved.
-#Fri Jul 16 10:03:09 PDT 2010
-currentVersion=2.0.2
-version=2.0.2
-isNative=false
-name=xml_sax_parser
-keywords=xml sax parser
-onDevice=true
-homepage=http\://www.saxproject.org
diff --git a/xml/src/main/java/org/xmlpull/ThirdPartyProject.prop b/xml/src/main/java/org/xmlpull/ThirdPartyProject.prop
deleted file mode 100644
index 9ccbf09..0000000
--- a/xml/src/main/java/org/xmlpull/ThirdPartyProject.prop
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2010 Google Inc. All Rights Reserved.
-#Fri Jul 16 10:03:09 PDT 2010
-currentVersion=1.1.3.4c
-version=Unknown
-isNative=false
-name=xml_pull_parser
-keywords=xml pull parser
-onDevice=true
-homepage=http\://xmlpull.org