diff options
author | Steve Kondik <shade@chemlab.org> | 2012-11-18 20:32:56 -0800 |
---|---|---|
committer | Steve Kondik <shade@chemlab.org> | 2012-11-18 20:32:56 -0800 |
commit | e888585a72df3786111230769d73fee92dbbfb39 (patch) | |
tree | 9be9526eddbc4d94771e034c6377e776323303d3 | |
parent | 6218bfec34349602e4aa7af9af632fe65549025a (diff) | |
parent | 548fbbd0c596c1ec1bd4527de6ed839f509d1be8 (diff) | |
download | libcore-e888585a72df3786111230769d73fee92dbbfb39.zip libcore-e888585a72df3786111230769d73fee92dbbfb39.tar.gz libcore-e888585a72df3786111230769d73fee92dbbfb39.tar.bz2 |
Merge branch 'jb-mr1-release' of https://android.googlesource.com/platform/libcore into mr1
Change-Id: I19ebeda37b4c3efa42f91ceb5d366ddccd699249
445 files changed, 20324 insertions, 7465 deletions
@@ -30,6 +30,7 @@ endef 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..26a586a 100644 --- a/JavaLibrary.mk +++ b/JavaLibrary.mk @@ -55,9 +55,11 @@ core_resource_dirs := $(call all-core-resource-dirs,main) test_resource_dirs := $(call all-core-resource-dirs,test) ifeq ($(EMMA_INSTRUMENT),true) +ifneq ($(EMMA_INSTRUMENT_STATIC),true) core_src_files += $(call all-java-files-under, ../external/emma/core ../external/emma/pregenerated) core_resource_dirs += ../external/emma/core/res ../external/emma/pregenerated/res endif +endif local_javac_flags=-encoding UTF-8 #local_javac_flags+=-Xlint:all -Xlint:-serial,-deprecation,-unchecked @@ -78,10 +80,9 @@ LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_DX_FLAGS := --core-library -LOCAL_NO_EMMA_INSTRUMENT := true -LOCAL_NO_EMMA_COMPILE := true LOCAL_MODULE_TAGS := optional LOCAL_MODULE := core +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk include $(BUILD_JAVA_LIBRARY) @@ -98,8 +99,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc mockwebserver LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests -LOCAL_NO_EMMA_INSTRUMENT := true -LOCAL_NO_EMMA_COMPILE := true +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk include $(BUILD_STATIC_JAVA_LIBRARY) # This one's tricky. One of our tests needs to have a @@ -134,12 +134,11 @@ ifeq ($(WITH_HOST_DALVIK),true) LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_DX_FLAGS := --core-library - LOCAL_NO_EMMA_INSTRUMENT := true - LOCAL_NO_EMMA_COMPILE := true LOCAL_BUILD_HOST_DEX := true LOCAL_MODULE_TAGS := optional LOCAL_MODULE := core-hostdex + LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk include $(BUILD_HOST_JAVA_LIBRARY) @@ -151,10 +150,9 @@ ifeq ($(WITH_HOST_DALVIK),true) 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_NO_EMMA_INSTRUMENT := true - LOCAL_NO_EMMA_COMPILE := true + LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk LOCAL_BUILD_HOST_DEX := true include $(BUILD_HOST_JAVA_LIBRARY) endif @@ -190,6 +188,7 @@ LOCAL_JAVACFLAGS := $(local_javac_flags) 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 @@ endef # 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 @@ $(foreach dir, \ 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_SHARED_LIBRARIES := $(core_shared_libraries) libexpat libicuuc libicui18n 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 @@ ifeq ($(WITH_HOST_DALVIK),true) 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 @@ public class BaseDexClassLoader extends ClassLoader { /** 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 @@ public class BaseDexClassLoader extends ClassLoader { super(parent); this.originalPath = dexPath; + this.originalLibraryPath = libraryPath; this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); } @@ -58,7 +62,7 @@ public class BaseDexClassLoader extends ClassLoader { 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 @@ public class BaseDexClassLoader extends ClassLoader { @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 @@ public final class CloseGuard { /** * 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 @@ public final class CloseGuard { * 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 @@ public final class VMRuntime { * @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..c06314e 100644 --- a/dalvik/src/main/java/dalvik/system/Zygote.java +++ b/dalvik/src/main/java/dalvik/system/Zygote.java @@ -41,6 +41,15 @@ public class Zygote { /** Enable logging of third-party JNI activity. */ public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4; + /** No external storage should be mounted. */ + public static final int MOUNT_EXTERNAL_NONE = 0; + /** Single-user external storage should be mounted. */ + public static final int MOUNT_EXTERNAL_SINGLEUSER = 1; + /** Multi-user external storage should be mounted. */ + public static final int MOUNT_EXTERNAL_MULTIUSER = 2; + /** All multi-user external storage should be mounted. */ + public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3; + /** * When set by the system server, all subsequent apps will be launched in * VM safe mode. @@ -107,31 +116,24 @@ public class Zygote { * 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) { + public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags, + int[][] rlimits, int mountExternal, String seInfo, String niceName) { preFork(); - int pid = nativeForkAndSpecialize(uid, gid, gids, debugFlags, rlimits); + int pid = nativeForkAndSpecialize( + uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName); postFork(); return pid; } - native public static int nativeForkAndSpecialize(int uid, int gid, - int[] gids, int debugFlags, int[][] rlimits); - - /** - * Forks a new VM instance. - * @deprecated use {@link Zygote#forkAndSpecialize(int, int, int[], int, int[][])} - */ - @Deprecated - 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); - } + native public static int nativeForkAndSpecialize(int uid, int gid, int[] gids, int debugFlags, + int[][] rlimits, int mountExternal, String seInfo, String niceName); /** * Special method to start the system server process. In addition to the @@ -156,31 +158,17 @@ public class Zygote { * @return 0 if this is the child, pid of the child * if this is the parent, or -1 on error. */ - public static int forkSystemServer(int uid, int gid, int[] gids, - int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities) { + public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags, + int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { preFork(); - int pid = nativeForkSystemServer(uid, gid, gids, debugFlags, rlimits, - permittedCapabilities, - effectiveCapabilities); + int pid = nativeForkSystemServer( + uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities); postFork(); return pid; } - /** - * Special method to start the system server process. - * @deprecated use {@link Zygote#forkSystemServer(int, int, int[], int, int[][])} - */ - @Deprecated - 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); - } - - native public static int nativeForkSystemServer(int uid, int gid, - int[] gids, int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities); + native public static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags, + int[][] rlimits, long permittedCapabilities, long effectiveCapabilities); /** * Executes "/system/bin/sh -c <command>" using the exec() system call. diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt index 6fc48cd..e1d81e0 100644 --- a/expectations/brokentests.txt +++ b/expectations/brokentests.txt @@ -8,7 +8,12 @@ bug: 5834665 }, { - description: "libcore.java.net.URLConnectionTest#testServerShutdownInput fails on ICL27 mysid-userdebug", + description: "FIONREAD/SIOCINQ returns the wrong result on sockets (5731252 is the root cause of 5534202)", + name: "libcore.java.net.SocketTest#testAvailable", + bug: 5731252 +}, +{ + description: "libcore.java.net.URLConnectionTest#testServerShutdownInput fails on ICL27 mysid-userdebug (5534202 is caused by 5731252)", name: "libcore.java.net.URLConnectionTest#testServerShutdownInput", bug: 5534202 }, diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt index fe110ea..5e24be3 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 @@ -200,15 +194,6 @@ bug: 2400429 }, { - description: "Concurrent close tests fail on the device", - names: [ - "libcore.java.net.ConcurrentCloseTest#test_connect", - "libcore.java.net.ConcurrentCloseTest#test_connect_nonBlocking" - ], - modes: [ "device" ], - bug: 3044772 -}, -{ description: "HTTPS connections should not be pooled.", name: "libcore.java.net.URLConnectionTest#testConnectViaHttpsReusingConnectionsDifferentFactories", bug: 3042192 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 class PropertyChangeSupport implements Serializable { */ 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 @@ public class BufferedWriter extends Writer { /** * 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 @@ public class BufferedWriter extends Writer { 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 @@ public class BufferedWriter extends Writer { 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 @@ public class ByteArrayOutputStream extends OutputStream { /** * 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 class File implements Serializable, Comparable<File> { */ 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 @@ public class File implements Serializable, Comparable<File> { } /** - * 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..e4eb06c 100644 --- a/luni/src/main/java/java/io/FileDescriptor.java +++ b/luni/src/main/java/java/io/FileDescriptor.java @@ -68,7 +68,11 @@ public final class FileDescriptor { */ 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 @@ public class InputStreamReader extends Reader { /** * 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.Array; 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 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec 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 class ObjectInputStream extends InputStream implements ObjectInput, Objec 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 @@ public class ObjectStreamClass implements Serializable { 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 @@ public class ObjectStreamField implements Comparable<Object> { * 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 @@ public class ObjectStreamField implements Comparable<Object> { * @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 @@ public class ObjectStreamField implements Comparable<Object> { */ 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 @@ public class OutputStreamWriter extends Writer { /** * 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 @@ public class OutputStreamWriter extends Writer { /** * 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 class PipedOutputStream extends OutputStream { */ 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 class PipedWriter extends Writer { */ 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 class PrintStream extends FilterOutputStream implements Appendable, Close public PrintStream(OutputStream out) { super(out); if (out == null) { - throw new NullPointerException(); + throw new NullPointerException("out == null"); } } @@ -80,14 +80,14 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close 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 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close * @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 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close /** * 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 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close /** * 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 @@ public abstract class Reader implements Readable, Closeable { */ 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 class SequenceInputStream extends InputStream { */ 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 @@ public class SequenceInputStream extends InputStream { if (e.hasMoreElements()) { in = e.nextElement(); if (in == null) { - throw new NullPointerException(); + throw new NullPointerException("element is null"); } } } @@ -112,7 +112,7 @@ public class SequenceInputStream extends InputStream { 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 class StreamTokenizer { public StreamTokenizer(InputStream is) { this(); if (is == null) { - throw new NullPointerException(); + throw new NullPointerException("is == null"); } inStream = is; } @@ -194,7 +194,7 @@ public class StreamTokenizer { 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 class StringBufferInputStream extends InputStream { */ 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 @@ public abstract class Writer implements Appendable, Closeable, Flushable { */ 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 @@ abstract class AbstractStringBuilder { AbstractStringBuilder(int capacity) { if (capacity < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(capacity)); } value = new char[capacity]; } @@ -437,7 +437,7 @@ abstract class AbstractStringBuilder { } 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 @@ public final class Character implements Serializable, Comparable<Character> { */ protected Subset(String string) { if (string == null) { - throw new NullPointerException(); + throw new NullPointerException("string == null"); } name = string; } @@ -1502,7 +1502,7 @@ public final class Character implements Serializable, Comparable<Character> { */ 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 final class Character implements Serializable, Comparable<Character> { */ 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 final class Character implements Serializable, Comparable<Character> { */ 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 final class Character implements Serializable, Comparable<Character> { */ 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 final class Character implements Serializable, Comparable<Character> { */ 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 final class Character implements Serializable, Comparable<Character> { */ 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 final class Character implements Serializable, Comparable<Character> { 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 final class Character implements Serializable, Comparable<Character> { 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 final class Character implements Serializable, Comparable<Character> { */ 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 @@ public abstract class ClassLoader { */ 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 @@ import libcore.util.EmptyArray; * @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 @@ public final class Daemons { @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; - } - } + 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; + } + } + } - 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; + 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; } + } + } + } - if (object != FinalizerDaemon.INSTANCE.finalizingObject - || VMRuntime.getRuntime().isDebuggerActive()) { - continue; + 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; } - - // 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) { } } } + + 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 @@ public abstract class Enum<E extends Enum<E>> implements Serializable, Comparabl 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 @@ public abstract class Enum<E extends Enum<E>> implements Serializable, Comparabl * 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/Object.java b/luni/src/main/java/java/lang/Object.java index 7f4b490..4bca034 100644 --- a/luni/src/main/java/java/lang/Object.java +++ b/luni/src/main/java/java/lang/Object.java @@ -361,7 +361,7 @@ public class Object { * @see java.lang.Thread */ public final void wait() throws InterruptedException { - wait(0 ,0); + wait(0, 0); } /** 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 final class ProcessBuilder { */ public ProcessBuilder(List<String> command) { if (command == null) { - throw new NullPointerException(); + throw new NullPointerException("command == null"); } this.command = command; @@ -102,7 +102,7 @@ public final class ProcessBuilder { */ 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 @@ final class ProcessManager { 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 @@ final class ProcessManager { 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 class Runtime { 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 @@ public class Runtime { /* * 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 @@ public class Runtime { 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 class Runtime { 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 class Runtime { 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 @@ public final class StackTraceElement implements Serializable { * 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/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 @@ public final class String implements Serializable, Comparable<String>, CharSeque * 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 @@ outer: */ 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 @@ outer: */ 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 @@ outer: */ public boolean contentEquals(CharSequence cs) { if (cs == null) { - throw new NullPointerException(); + throw new NullPointerException("cs == null"); } int len = cs.length(); @@ -1922,7 +1912,7 @@ outer: */ 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 @@ outer: */ 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 @@ public final class StringBuilder extends AbstractStringBuilder implements * 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 @@ public final class StringBuilder extends AbstractStringBuilder implements * @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 @@ package java.lang; 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 @@ public final class System { 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 final class System { */ 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 @@ public final class System { 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 class Thread implements Runnable { */ 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 class Thread implements Runnable { */ 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 class Thread implements Runnable { */ 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 class Thread implements Runnable { */ 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 class Thread implements Runnable { */ 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 class Thread implements Runnable { */ 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.ObjectOutputStream; 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 @@ public class Throwable implements java.io.Serializable { * 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 class Throwable implements java.io.Serializable { */ 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 class Throwable implements java.io.Serializable { */ 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 @@ public class Throwable implements java.io.Serializable { * @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 @@ package java.lang.ref; * @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(); - private T zombie; + // 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; - private FinalizerReference prev; + // The links used to construct the list. + private FinalizerReference<?> prev; + private FinalizerReference<?> next; - private FinalizerReference next; + // When the GC wants something finalized, it moves it from the 'referent' field to + // the 'zombie' field instead. + private T zombie; 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 final class FinalizerReference<T> extends Reference<T> { } } - 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 @@ public final class FinalizerReference<T> extends Reference<T> { } /** - * 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 @@ public abstract class Reference<T> { * 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 @@ public abstract class Reference<T> { 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 @@ public class ReferenceQueue<T> { * * @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 @@ public class ReferenceQueue<T> { } /** @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 @@ public interface GenericArrayType extends Type { * 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 @@ public class Proxy implements Serializable { 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 @@ public class Proxy implements Serializable { 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 class Proxy implements Serializable { */ 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 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial 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 class BigDecimal extends Number implements Comparable<BigDecimal>, Serial */ 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 class BigDecimal extends Number implements Comparable<BigDecimal>, Serial 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 class BigDecimal extends Number implements Comparable<BigDecimal>, Serial */ 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 @@ final class BigInt { */ 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 @@ public interface CookieStore { * @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 @@ public class DatagramSocket { 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 class DatagramSocket { */ 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 @@ public class DatagramSocket { */ 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 @@ public final class NetworkInterface extends Object { 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 @@ public final class NetworkInterface extends Object { // 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 @@ public final class NetworkInterface extends Object { 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 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl { 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 class URISyntaxException extends Exception { 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 class URISyntaxException extends Exception { 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 @@ public class URLClassLoader extends SecureClassLoader { 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 @@ public abstract class URLConnection { } /** - * 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 @@ public abstract class URLConnection { * 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 @@ public abstract class URLConnection { } /** - * 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 @@ public abstract class Buffer { 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 abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer */ 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 abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer */ 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 abstract class CharBuffer extends Buffer implements */ 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 abstract class CharBuffer extends Buffer implements */ 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 @@ public abstract class CharBuffer extends Buffer implements 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 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann */ 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 abstract class DoubleBuffer extends Buffer implements */ 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 abstract class DoubleBuffer extends Buffer implements */ 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 @@ final class FileChannelImpl extends FileChannel { 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 @@ final class FileChannelImpl extends FileChannel { 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 abstract class FloatBuffer extends Buffer implements */ 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 abstract class FloatBuffer extends Buffer implements */ 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 abstract class IntBuffer extends Buffer implements Comparable<IntBuffer> */ 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 abstract class IntBuffer extends Buffer implements Comparable<IntBuffer> */ 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 abstract class LongBuffer extends Buffer implements */ 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 abstract class LongBuffer extends Buffer implements */ 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 @@ public abstract class MappedByteBuffer extends ByteBuffer { 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 @@ final class NIOAccess { * 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 @@ final class NIOAccess { * 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 @@ final class NIOAccess { /** * 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/PipeImpl.java b/luni/src/main/java/java/nio/PipeImpl.java index 50028f4..d4dde3b 100644 --- a/luni/src/main/java/java/nio/PipeImpl.java +++ b/luni/src/main/java/java/nio/PipeImpl.java @@ -19,8 +19,8 @@ package java.nio; import java.io.Closeable; import java.io.FileDescriptor; import java.io.IOException; -import java.nio.channels.FileChannel; import java.nio.channels.Pipe; +import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import libcore.io.ErrnoException; import libcore.io.IoUtils; @@ -34,13 +34,16 @@ final class PipeImpl extends Pipe { private final PipeSinkChannel sink; private final PipeSourceChannel source; - public PipeImpl() throws IOException { + public PipeImpl(SelectorProvider selectorProvider) throws IOException { try { - FileDescriptor[] fds = Libcore.os.pipe(); - // Which fd is used for which channel is important. Unix pipes are only guaranteed to be - // unidirectional, and indeed are only unidirectional on Linux. - this.sink = new PipeSinkChannel(fds[1]); - this.source = new PipeSourceChannel(fds[0]); + FileDescriptor fd1 = new FileDescriptor(); + FileDescriptor fd2 = new FileDescriptor(); + Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd1, fd2); + + // It doesn't matter which file descriptor we use for which end; + // they're guaranteed to be indistinguishable. + this.sink = new PipeSinkChannel(selectorProvider, fd1); + this.source = new PipeSourceChannel(selectorProvider, fd2); } catch (ErrnoException errnoException) { throw errnoException.rethrowAsIOException(); } @@ -54,27 +57,14 @@ final class PipeImpl extends Pipe { return source; } - /** - * FileChannelImpl doesn't close its fd itself; it calls close on the object it's given. - */ - private static class FdCloser implements Closeable { - private final FileDescriptor fd; - private FdCloser(FileDescriptor fd) { - this.fd = fd; - } - public void close() throws IOException { - IoUtils.close(fd); - } - } - private class PipeSourceChannel extends Pipe.SourceChannel implements FileDescriptorChannel { private final FileDescriptor fd; - private final FileChannel channel; + private final SocketChannel channel; - private PipeSourceChannel(FileDescriptor fd) throws IOException { - super(SelectorProvider.provider()); + private PipeSourceChannel(SelectorProvider selectorProvider, FileDescriptor fd) throws IOException { + super(selectorProvider); this.fd = fd; - this.channel = NioUtils.newFileChannel(new FdCloser(fd), fd, O_RDONLY); + this.channel = new SocketChannelImpl(selectorProvider, fd); } @Override protected void implCloseSelectableChannel() throws IOException { @@ -104,12 +94,12 @@ final class PipeImpl extends Pipe { private class PipeSinkChannel extends Pipe.SinkChannel implements FileDescriptorChannel { private final FileDescriptor fd; - private final FileChannel channel; + private final SocketChannel channel; - private PipeSinkChannel(FileDescriptor fd) throws IOException { - super(SelectorProvider.provider()); + private PipeSinkChannel(SelectorProvider selectorProvider, FileDescriptor fd) throws IOException { + super(selectorProvider); this.fd = fd; - this.channel = NioUtils.newFileChannel(new FdCloser(fd), fd, O_WRONLY); + this.channel = new SocketChannelImpl(selectorProvider, fd); } @Override protected void implCloseSelectableChannel() throws IOException { 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 @@ final class SelectorImpl extends AbstractSelector { @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/SelectorProviderImpl.java b/luni/src/main/java/java/nio/SelectorProviderImpl.java index 03003fe..b7c34e8 100644 --- a/luni/src/main/java/java/nio/SelectorProviderImpl.java +++ b/luni/src/main/java/java/nio/SelectorProviderImpl.java @@ -34,7 +34,7 @@ public final class SelectorProviderImpl extends SelectorProvider { } public Pipe openPipe() throws IOException { - return new PipeImpl(); + return new PipeImpl(this); } public AbstractSelector openSelector() throws IOException { 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 abstract class ShortBuffer extends Buffer implements */ 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 abstract class ShortBuffer extends Buffer implements */ 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..233daf0 100644 --- a/luni/src/main/java/java/nio/SocketChannelImpl.java +++ b/luni/src/main/java/java/nio/SocketChannelImpl.java @@ -103,6 +103,15 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { } /* + * Constructor for use by Pipe.SinkChannel and Pipe.SourceChannel. + */ + public SocketChannelImpl(SelectorProvider selectorProvider, FileDescriptor existingFd) throws IOException { + super(selectorProvider); + status = SOCKET_STATUS_CONNECTED; + fd = existingFd; + } + + /* * Getting the internal Socket If we have not the socket, we create a new * one. */ @@ -318,7 +327,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { @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 +421,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { */ 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 final class Channels { 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 final class Channels { 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 @@ public final class Channels { ChannelInputStream(ReadableByteChannel channel) { if (channel == null) { - throw new NullPointerException(); + throw new NullPointerException("channel == null"); } this.channel = channel; } @@ -247,7 +247,7 @@ public final class Channels { ChannelOutputStream(WritableByteChannel channel) { if (channel == null) { - throw new NullPointerException(); + throw new NullPointerException("channel == null"); } this.channel = channel; } @@ -289,7 +289,7 @@ public final class Channels { InputStreamChannel(InputStream inputStream) { if (inputStream == null) { - throw new NullPointerException(); + throw new NullPointerException("inputStream == null"); } this.inputStream = inputStream; } @@ -328,7 +328,7 @@ public final class Channels { 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 @@ public abstract class FileLock { */ 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 abstract class CharsetDecoder { */ 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 abstract class CharsetDecoder { */ 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 @@ package java.nio.charset; 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 @@ final class CharsetDecoderICU extends CharsetDecoder { // 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 @@ final class CharsetDecoderICU extends CharsetDecoder { } 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 @@ final class CharsetDecoderICU extends CharsetDecoder { input = null; allocatedInput = null; allocatedOutput = null; - ec = 0; inEnd = 0; outEnd = 0; } @@ -114,16 +109,14 @@ final class CharsetDecoderICU extends CharsetDecoder { 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 @@ final class CharsetDecoderICU extends CharsetDecoder { 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 @@ public abstract class CharsetEncoder { 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.ByteBuffer; 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 @@ final class CharsetEncoderICU extends CharsetEncoder { // 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 @@ final class CharsetEncoderICU extends CharsetEncoder { } 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 @@ final class CharsetEncoderICU extends CharsetEncoder { input = null; allocatedInput = null; allocatedOutput = null; - ec = 0; inEnd = 0; outEnd = 0; } @@ -142,16 +137,14 @@ final class CharsetEncoderICU extends CharsetEncoder { 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 @@ final class CharsetEncoderICU extends CharsetEncoder { 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 @@ package java.nio.charset; * * @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 @@ public class CoderResult { return r; } } - throw new IllegalArgumentException("Length must be greater than 0; was " + length); + throw new IllegalArgumentException("length <= 0: " + length); } /** @@ -149,7 +149,7 @@ public class CoderResult { 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 class AlgorithmParameterGenerator { 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 @@ public class AlgorithmParameterGenerator { 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 class AlgorithmParameters { 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 @@ public class AlgorithmParameters { 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 class AlgorithmParameters { 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 class KeyFactory { 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 @@ public class KeyFactory { 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 abstract class KeyPairGenerator extends KeyPairGeneratorSpi { 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 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi { 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 class KeyStore { */ 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 @@ public class KeyStore { 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 @@ public abstract class KeyStoreSpi { } 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 abstract class MessageDigest extends MessageDigestSpi { 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 @@ public abstract class MessageDigest extends MessageDigestSpi { 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 abstract class MessageDigest extends MessageDigestSpi { */ 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.Enumeration; 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 @@ public abstract class Provider extends Properties { // 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 abstract class Provider extends Properties { */ 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { */ 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 @@ public abstract class Provider extends Properties { */ 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { 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 @@ public abstract class Provider extends Properties { 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 abstract class Provider extends Properties { */ 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 abstract class Provider extends Properties { */ 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 class SecureRandom extends Random { */ public SecureRandom() { super(0); - Services.refresh(); Provider.Service service = Services.getSecureRandomService(); if (service == null) { this.provider = null; @@ -154,7 +153,7 @@ public class SecureRandom extends Random { */ 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 @@ public class SecureRandom extends Random { 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 final class Security { */ 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 final class Security { */ 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 abstract class Signature extends SignatureSpi { 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 abstract class Signature extends SignatureSpi { 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 abstract class Signature extends SignatureSpi { 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 abstract class Signer extends Identity { */ 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 class CertPathBuilder { 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 class CertPathBuilder { 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 class CertPathBuilder { 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 class CertPathValidator { 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 @@ public class CertPathValidator { 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 class CertStore { 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 @@ public class CertStore { 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 @@ public class CertStore { 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 class CertificateFactory { 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 @@ public class CertificateFactory { 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 class CertificateFactory { 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 @@ public class CollectionCertStoreParameters implements CertStoreParameters { * 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 @@ public class LDAPCertStoreParameters implements CertStoreParameters { * 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 @@ public class LDAPCertStoreParameters implements CertStoreParameters { * 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 class PKIXCertPathBuilderResult extends PKIXCertPathValidatorResult 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 abstract class X509CRL extends CRL implements X509Extension { */ 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 class ECGenParameterSpec implements AlgorithmParameterSpec { 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 @@ public class DataTruncation extends SQLWarning implements Serializable { /** * 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 @@ public class DataTruncation extends SQLWarning implements Serializable { /** * 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 class DriverManager { */ 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 class AttributedString { */ 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 class AttributedString { 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 class AttributedString { */ 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 class AttributedString { 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 abstract class Collator implements Comparator<Object>, Cloneable { */ 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/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 class DateFormatSymbols implements Serializable, Cloneable { */ 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 class DateFormatSymbols implements Serializable, Cloneable { */ public void setLocalPatternChars(String data) { if (data == null) { - throw new NullPointerException(); + throw new NullPointerException("data == null"); } localPatternChars = data; } @@ -471,7 +471,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { */ 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 c0d67b8..948bec1 100644 --- a/luni/src/main/java/java/text/DecimalFormat.java +++ b/luni/src/main/java/java/text/DecimalFormat.java @@ -642,7 +642,7 @@ public class DecimalFormat extends NumberFormat { @Override public AttributedCharacterIterator formatToCharacterIterator(Object object) { if (object == null) { - throw new NullPointerException(); + throw new NullPointerException("object == null"); } return ndf.formatToCharacterIterator(object); } @@ -1236,7 +1236,7 @@ public class DecimalFormat extends NumberFormat { */ 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. 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 class DecimalFormatSymbols implements Cloneable, Serializable { */ 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 class DecimalFormatSymbols implements Cloneable, Serializable { */ 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 class DecimalFormatSymbols implements Cloneable, Serializable { */ 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 @@ public class MessageFormat extends Format { @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 54a4849..c285e3d 100644 --- a/luni/src/main/java/java/text/NumberFormat.java +++ b/luni/src/main/java/java/text/NumberFormat.java @@ -566,7 +566,7 @@ public abstract class NumberFormat extends Format { @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 class RuleBasedCollator extends Collator { */ 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 class RuleBasedCollator extends Collator { */ 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 class RuleBasedCollator extends Collator { */ 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 @@ public class RuleBasedCollator extends Collator { */ @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 5cad44b..f682c0b 100644 --- a/luni/src/main/java/java/text/SimpleDateFormat.java +++ b/luni/src/main/java/java/text/SimpleDateFormat.java @@ -468,7 +468,7 @@ public class SimpleDateFormat extends DateFormat { @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); @@ -1067,24 +1067,42 @@ public class SimpleDateFormat extends DateFormat { } 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); @@ -1171,14 +1189,18 @@ public class SimpleDateFormat extends DateFormat { } 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 abstract class AbstractQueue<E> */ 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 @@ package java.util; // 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 @@ import java.io.*; * @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 @@ public class ArrayDeque<E> extends AbstractCollection<E> * 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> * sufficient to hold 16 elements. */ public ArrayDeque() { - elements = (E[]) new Object[16]; + elements = new Object[16]; } /** @@ -195,7 +193,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> */ 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 class ArrayDeque<E> extends AbstractCollection<E> */ 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 class ArrayDeque<E> extends AbstractCollection<E> 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 class ArrayDeque<E> extends AbstractCollection<E> 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> * @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 @@ public class ArrayDeque<E> extends AbstractCollection<E> 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> */ 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 class ArrayDeque<E> extends AbstractCollection<E> 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> * 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> * 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 class ArrayDeque<E> extends AbstractCollection<E> */ 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> * 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> /** * 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 @@ public class ArrayDeque<E> extends AbstractCollection<E> // 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 @@ public class Arrays { ArrayList(E[] storage) { if (storage == null) { - throw new NullPointerException(); + throw new NullPointerException("storage == null"); } a = storage; } @@ -2459,7 +2459,7 @@ public class Arrays { */ 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 class Arrays { */ 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 class Arrays { */ 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 class Arrays { */ 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 class Arrays { */ 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 class Arrays { */ 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 class Arrays { */ 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 class Arrays { */ 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 class Arrays { */ 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 class Arrays { 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 class BitSet implements Serializable, Cloneable { */ 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 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca return getTimeInMillis() == cal.getTimeInMillis() && isLenient() == cal.isLenient() && getFirstDayOfWeek() == cal.getFirstDayOfWeek() - && getMinimalDaysInFirstWeek() == cal - .getMinimalDaysInFirstWeek() + && getMinimalDaysInFirstWeek() == cal.getMinimalDaysInFirstWeek() && getTimeZone().equals(cal.getTimeZone()); } @@ -903,11 +902,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca } /** - * 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 abstract class Calendar implements Serializable, Cloneable, Comparable<Ca */ 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 @@ public class Collections { @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 @@ public class Collections { @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 class Collections { 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 class Collections { */ 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 class Collections { */ 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 class Collections { */ 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 class Collections { 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 class Collections { */ 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 class Collections { 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 @@ public class Collections { @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 class Collections { 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 @@ public class Collections { @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 class Collections { 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 class Collections { */ 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 class Collections { */ 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 @@ public class Collections { 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 @@ public class Collections { 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 class Collections { 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 @@ public interface Deque<E> extends Queue<E> { * <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 @@ final class DualPivotQuicksort { 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 @@ final class DualPivotQuicksort { 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 class DuplicateFormatFlagsException extends IllegalFormatException { */ 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 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements @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 class FormatFlagsConversionMismatchException extends */ 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 final class Formatter implements Closeable, Flushable { */ 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 @@ public class GregorianCalendar extends Calendar { 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 @@ public class GregorianCalendar extends Calendar { throw new IllegalArgumentException(); } - isCached = false; - if (field == ERA) { complete(); if (fields[ERA] == AD) { @@ -468,19 +458,8 @@ public class GregorianCalendar extends Calendar { 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 @@ public class GregorianCalendar extends Calendar { } } - 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 @@ public class GregorianCalendar extends Calendar { 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 @@ public class GregorianCalendar extends Calendar { 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 @@ public class GregorianCalendar extends Calendar { } /** - * 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 @@ public class GregorianCalendar extends Calendar { && 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 @@ public class GregorianCalendar extends Calendar { 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 @@ public class GregorianCalendar extends Calendar { throw new IllegalArgumentException(); } - isCached = false; - complete(); int days, day, mod, maxWeeks, newWeek; int max = -1; @@ -1410,13 +1229,10 @@ public class GregorianCalendar extends Calendar { /** * 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 @@ public class GregorianCalendar extends Calendar { } 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 @@ public class GregorianCalendar extends Calendar { 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 class Hashtable<K, V> extends Dictionary<K, V> */ public synchronized boolean containsValue(Object value) { if (value == null) { - throw new NullPointerException(); + throw new NullPointerException("value == null"); } HashtableEntry[] tab = table; @@ -361,8 +361,10 @@ public class Hashtable<K, V> extends Dictionary<K, V> * @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 @@ public class Hashtable<K, V> extends Dictionary<K, V> * 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 class Hashtable<K, V> extends Dictionary<K, V> 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 class IllegalFormatConversionException extends IllegalFormatException 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 class IllegalFormatFlagsException extends IllegalFormatException implemen */ 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 abstract class ListResourceBundle extends ResourceBundle { 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/Locale.java b/luni/src/main/java/java/util/Locale.java index 6b20a1c..0fbe2f5 100644 --- a/luni/src/main/java/java/util/Locale.java +++ b/luni/src/main/java/java/util/Locale.java @@ -396,6 +396,17 @@ public final class Locale implements Cloneable, Serializable { if (languageCode.isEmpty()) { return ""; } + + // Last-minute workaround for http://b/7291355 in jb-mr1. + // This isn't right for all languages, but it's right for en and tl. + // We should have more CLDR data in a future release, but we'll still + // probably want to have frameworks/base translate the obsolete tl and + // tl-rPH locales to fil and fil-rPH at runtime, at which point + // libcore and icu4c will just do the right thing. + if (languageCode.equals("tl")) { + return "Filipino"; + } + String result = ICU.getDisplayLanguageNative(toString(), locale.toString()); if (result == null) { // TODO: do we need to do this, or does ICU do it for us? result = ICU.getDisplayLanguageNative(toString(), Locale.getDefault().toString()); 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 class MissingFormatArgumentException extends IllegalFormatException { */ 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 class MissingFormatWidthException extends IllegalFormatException { */ 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 @@ package java.util; * 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 @@ package java.util; * 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 class Observable { */ 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 class PriorityQueue<E> extends AbstractQueue<E> implements Serializable { */ 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 @@ public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable { 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 class Properties extends Hashtable<Object, Object> { */ 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 @@ public class Properties extends Hashtable<Object, Object> { @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 class Properties extends Hashtable<Object, Object> { 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 class Properties extends Hashtable<Object, Object> { 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 class PropertyResourceBundle extends ResourceBundle { */ 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 @@ public class Random implements Serializable { * 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 abstract class ResourceBundle { */ 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 abstract class ResourceBundle { 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 @@ public abstract class ResourceBundle { @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 @@ public abstract class ResourceBundle { * {@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 abstract class ResourceBundle { */ public List<String> getFormats(String baseName) { if (baseName == null) { - throw new NullPointerException(); + throw new NullPointerException("baseName == null"); } return format; } @@ -838,8 +844,10 @@ public abstract class ResourceBundle { * 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 @@ public abstract class ResourceBundle { 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 @@ public abstract class ResourceBundle { * 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 @@ public abstract class ResourceBundle { 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 @@ public abstract class ResourceBundle { 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 abstract class ResourceBundle { */ 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 final class Scanner implements Iterator<String> { */ public Scanner(Readable src) { if (src == null) { - throw new NullPointerException(); + throw new NullPointerException("src == null"); } input = src; initialization(); @@ -1664,7 +1664,7 @@ public final class Scanner implements Iterator<String> { */ 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 @@ public final class Scanner implements Iterator<String> { */ 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 @@ public final class ServiceLoader<S> implements Iterable<S> { // 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 @@ public class SimpleTimeZone extends TimeZone { 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 class StringTokenizer implements Enumeration<Object> { */ 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 class StringTokenizer implements Enumeration<Object> { */ 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 class StringTokenizer implements Enumeration<Object> { */ 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 @@ import libcore.util.ZoneInfoDB; * <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 @@ import libcore.util.ZoneInfoDB; 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 abstract class TimeZone implements Serializable, Cloneable { */ 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 @@ public abstract class TimeZone implements Serializable, Cloneable { } /** - * 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>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 apply the + * <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 class TimeZone implements Serializable, Cloneable { 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 @@ public abstract class TimeZone implements Serializable, Cloneable { 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 != '-') { - return null; - } - int[] position = new int[1]; - String formattedName = formatTimeZoneName(id, 4); - int hour = parseNumber(formattedName, 4, position); - if (hour < 0 || hour > 23) { + Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id); + if (!m.matches()) { 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; - } - raw += minute * 60000; - } else if (hour >= 30 || index > 6) { - raw = (hour / 100 * 3600000) + (hour % 100 * 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(':'); + int hour; + int minute = 0; + try { + hour = Integer.parseInt(m.group(1)); + if (m.group(3) != null) { + minute = Integer.parseInt(m.group(3)); } - index++; + } catch (NumberFormatException impossible) { + throw new AssertionError(impossible); } - if (buf.toString().indexOf(":") == -1) { - buf.append(':'); - buf.append("00"); + if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { + return null; } - if (buf.toString().indexOf(":") == 5) { - buf.insert(4, '0'); + char sign = id.charAt(3); + int raw = (hour * 3600000) + (minute * 60000); + if (sign == '-') { + raw = -raw; } - 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 class TimeZone implements Serializable, Cloneable { */ 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 abstract class TimeZone implements Serializable, Cloneable { */ 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 class Timer { */ 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 final class UUID implements Serializable, Comparable<UUID> { */ 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 final class UUID implements Serializable, Comparable<UUID> { */ 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 class UnknownFormatConversionException extends IllegalFormatException { */ 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 class UnknownFormatFlagsException extends IllegalFormatException { */ 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> /** 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> if (count == items.length) return false; else { - insert(e); + enqueue(e); return true; } } finally { @@ -293,7 +320,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> try { while (count == items.length) notFull.await(); - insert(e); + enqueue(e); } finally { lock.unlock(); } @@ -320,7 +347,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> return false; nanos = notFull.awaitNanos(nanos); } - insert(e); + enqueue(e); return true; } finally { lock.unlock(); @@ -331,7 +358,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> try { while (count == 0) notEmpty.await(); - return extract(); + return dequeue(); } finally { lock.unlock(); } @@ -359,7 +386,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> return null; nanos = notEmpty.awaitNanos(nanos); } - return extract(); + return dequeue(); } finally { lock.unlock(); } @@ -438,11 +465,15 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * @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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> 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; - } - if (n > 0) { - count -= n; - takeIndex = i; - notFull.signalAll(); + 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(); + } } - return n; } finally { lock.unlock(); } @@ -675,12 +705,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * 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 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } /** - * 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. + */ + class Itrs { + + /** + * 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; + } + } + + /** 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); + } + + /** + * 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; + } + 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; + } + + /** + * 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; + } + 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> { - 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 + /** 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 { - lastRet = -1; - if ((remaining = count) > 0) + 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() { - return remaining > 0; + // 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 (remaining <= 0) - throw new NoSuchElementException(); + if (!isDetached()) + incorporateDequeues(); + // assert nextIndex != NONE; + // assert lastItem == null; 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 + final int cursor = this.cursor; + if (cursor >= 0) { + nextItem = itemAt(nextIndex = cursor); + // assert nextItem != null; + this.cursor = incCursor(cursor); + } else { + nextIndex = NONE; + nextItem = null; } - else - lastItem = x; - while (--remaining > 0 && // skip over nulls - (nextItem = itemAt(nextIndex = inc(nextIndex))) == null) - ; - return x; } finally { lock.unlock(); } + return x; } public void remove() { + // assert lock.getHoldCount() == 0; final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { - int i = lastRet; - if (i == -1) + 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(); - 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); - } + // 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 @@ import java.util.*; * <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 @@ import java.util.*; * <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 @@ import java.util.*; * <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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * 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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * 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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * 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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * 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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * @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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * @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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * <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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * 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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * (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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * 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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * (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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * @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 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * 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 @@ import java.util.Queue; * <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 @@ import java.util.Queue; * 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 @@ import java.util.Queue; * 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 @@ public interface BlockingQueue<E> extends Queue<E> { * <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 @@ public interface BlockingQueue<E> extends Queue<E> { * @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 @@ public interface BlockingQueue<E> extends Queue<E> { * @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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> /* * 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** - * 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.) - * - * 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. + * 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. * - * - 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; + + /** + * The per-segment table. Elements are accessed via + * entryAt/setEntryAt providing volatile semantics. + */ + transient volatile HashEntry<K,V>[] table; /** - * 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 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> */ final float loadFactor; - Segment(int initialCapacity, float lf) { - loadFactor = lf; - setTable(HashEntry.<K,V>newArray(initialCapacity)); - } - - @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)]; + Segment(float lf, int threshold, HashEntry<K,V>[] tab) { + this.loadFactor = lf; + this.threshold = threshold; + this.table = tab; } - /** - * 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(); + final V put(K key, int hash, V value, boolean onlyIfAbsent) { + HashEntry<K,V> node = tryLock() ? null : + scanAndLockForPut(key, hash, value); + V oldValue; 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; + 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; } } - } - 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(); } + return oldValue; } - void rehash() { - HashEntry<K,V>[] oldTable = table; - int oldCapacity = oldTable.length; - if (oldCapacity >= MAXIMUM_CAPACITY) - return; - + /** + * Doubles size of table and repacks entries, also adding the + * given node to new table + */ + @SuppressWarnings("unchecked") + private void rehash(HashEntry<K,V> node) { /* - * 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. + * 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>[] newTable = HashEntry.newArray(oldCapacity<<1); - threshold = (int)(newTable.length * loadFactor); - int sizeMask = newTable.length - 1; + HashEntry<K,V>[] oldTable = table; + int oldCapacity = oldTable.length; + 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } } 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. + 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; + } + } + } 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; - 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 + break; } } - return oldValue; + } 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(); } } + } + + // 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); + } - 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(); + /** + * 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> ++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,33 +792,36 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * <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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @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; - } - } - } - if (cleanSweep) - return false; - } - // Resort to locking all segments - for (int i = 0; i < segments.length; ++i) - segments[i].lock(); - boolean found = false; + long previousSum = 0L; + int lockCount = 0; try { - for (int i = 0; i < segments.length; ++i) { - if (segments[i].containsValue(value)) { - found = true; - break; + 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 ((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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * <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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * 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 class ConcurrentHashMap<K, V> extends AbstractMap<K, V> */ 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 class ConcurrentHashMap<K, V> extends AbstractMap<K, V> */ 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** - * 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> /* ---------------- 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** - * 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 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 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 @@ import java.util.Queue; * 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 @@ public class ConcurrentLinkedDeque<E> 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 @@ public class ConcurrentLinkedDeque<E> 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 @@ public class ConcurrentLinkedDeque<E> // 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 @@ public class ConcurrentLinkedDeque<E> * 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 @@ public class ConcurrentLinkedDeque<E> 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 @@ public class ConcurrentLinkedDeque<E> 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 @@ import java.util.Queue; * <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 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> // 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 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> * 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 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> 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 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> 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 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { * 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 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { /** * 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 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { * @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 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { /** * 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 @@ import java.util.concurrent.atomic.*; * 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 @@ import java.util.concurrent.atomic.*; * <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 @@ import java.util.concurrent.atomic.*; * 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> } // 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * @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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> /* ---------------- 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> } /** - * 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> /** * 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * * @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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> */ 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> // 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> /* ---------------- 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 class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> 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 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> } // 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 @@ import sun.misc.Unsafe; * <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 @@ import sun.misc.Unsafe; * 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 @@ public class ConcurrentSkipListSet<E> * @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 class ConcurrentSkipListSet<E> 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 @@ public class ConcurrentSkipListSet<E> * @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); } - } - private void setMap(ConcurrentNavigableMap<E,Object> map) { - unsafe.putObjectVolatile(this, mapOffset, map); + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = ConcurrentSkipListSet.class; + mapOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("m")); + } catch (Exception e) { + throw new Error(e); + } } - } 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 @@ public class CopyOnWriteArraySet<E> extends AbstractSet<E> * 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 @@ public class CopyOnWriteArraySet<E> extends AbstractSet<E> * 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 @@ import java.util.concurrent.atomic.*; * until all workers have completed. * </ul> * - * <pre> + * <pre> {@code * class Driver { // ... * void main() throws InterruptedException { * CountDownLatch startSignal = new CountDownLatch(1); @@ -76,9 +75,7 @@ import java.util.concurrent.atomic.*; * } * * 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 @@ import java.util.concurrent.atomic.*; * 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 @@ import java.util.concurrent.atomic.*; * } * * 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 @@ import java.util.concurrent.locks.*; * * <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 @@ import java.util.concurrent.locks.*; * * 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 @@ import java.util.concurrent.locks.*; * {@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 @@ public class CyclicBarrier { 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 @@ public class CyclicBarrier { 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 @@ import java.util.*; * * <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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> 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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> 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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> 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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> } /** + * 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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> 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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> 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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> * <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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> } /** + * 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 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> */ 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 class DelayQueue<E extends Delayed> extends AbstractQueue<E> 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 @@ import java.util.concurrent.locks.LockSupport; * 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 @@ import java.util.concurrent.locks.LockSupport; * 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 @@ public class Exchanger<V> { * 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 @@ public class Exchanger<V> { * 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 @@ public class ExecutionException extends Exception { /** * 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 @@ package java.util.concurrent; * 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 @@ import java.security.PrivilegedExceptionAction; * 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 @@ import java.security.PrivilegedExceptionAction; * 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 @@ import java.security.PrivilegedExceptionAction; * // 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.AccessController; 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 @@ public class Executors { /** * 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 @@ public class Executors { } /** - * 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 @@ public class Executors { } /** - * 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 @@ public class Executors { /** * 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 @@ public class Executors { 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 @@ public class Executors { 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. + */ + + /** + * 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 <tt>FutureTask</tt> that will, upon running, execute the - * given <tt>Callable</tt>. + * 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 @@ public class FutureTask<V> implements RunnableFuture<V> { * 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 class FutureTask<V> implements RunnableFuture<V> { */ 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 @@ public class FutureTask<V> implements RunnableFuture<V> { 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; - - /** 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; - - /** 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; - - /** - * 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; - - 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; - } + 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 + + // assert state == INTERRUPTED; + + // 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(); + } - 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; - } + /** + * 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(); } + } - 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; - } - if (compareAndSetState(s, RAN)) { - result = v; - releaseShared(0); - done(); - 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; } + 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; - } - if (compareAndSetState(s, RAN)) { - exception = t; - releaseShared(0); - done(); - 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(); } - } - boolean innerCancel(boolean mayInterruptIfRunning) { - for (;;) { - int s = getState(); - if (ranOrCancelled(s)) - return false; - if (compareAndSetState(s, CANCELLED)) - break; + int s = state; + if (s > COMPLETING) { + if (q != null) + q.thread = null; + return s; } - if (mayInterruptIfRunning) { - Thread r = runner; - if (r != null) - r.interrupt(); + 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; + } + last = now; + LockSupport.parkNanos(this, nanos); } - releaseShared(0); - done(); - return true; + else + LockSupport.park(this); } + } - 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; + /** + * 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; } - set(result); - } else { - releaseShared(0); // cancel + break; } } + } - 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; - } + // 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.NoSuchElementException; 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 @@ import java.util.concurrent.locks.ReentrantLock; * <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 @@ public class LinkedBlockingDeque<E> /** * 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 @@ public class LinkedBlockingDeque<E> 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 @@ public class LinkedBlockingDeque<E> * 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 @@ public class LinkedBlockingDeque<E> /** * 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 @@ public class LinkedBlockingDeque<E> } /** - * 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 @@ public class LinkedBlockingDeque<E> } /** - * 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 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> // 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 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * 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 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * @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 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * 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 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> /** * 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> else siftDownUsingComparator(0, x, array, n, cmp); size = n; + return result; } - return result; } /** @@ -312,6 +311,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @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 class PriorityBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> lock.lockInterruptibly(); E result; try { - while ( (result = extract()) == null) + while ( (result = dequeue()) == null) notEmpty.await(); } finally { lock.unlock(); @@ -517,7 +515,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> 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 class PriorityBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @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 class PriorityBlockingQueue<E> extends AbstractQueue<E> } } - public String toString() { final ReentrantLock lock = this.lock; lock.lock(); @@ -708,7 +697,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> */ 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> 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 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } // 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 @@ public class RejectedExecutionException extends RuntimeException { /** * 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 @@ public class ScheduledThreadPoolExecutor * 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 class ScheduledThreadPoolExecutor } 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 @@ public class ScheduledThreadPoolExecutor 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 @@ public class ScheduledThreadPoolExecutor remove(task)) task.cancel(false); else - prestartCoreThread(); + ensurePrestart(); } } @@ -318,7 +321,7 @@ public class ScheduledThreadPoolExecutor if (!canRunInCurrentRunState(true) && remove(task)) task.cancel(false); else - prestartCoreThread(); + ensurePrestart(); } } @@ -396,7 +399,7 @@ public class ScheduledThreadPoolExecutor * @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 class ScheduledThreadPoolExecutor */ 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 class ScheduledThreadPoolExecutor */ 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 class ScheduledThreadPoolExecutor 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 @@ public class ScheduledThreadPoolExecutor 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 @@ public class ScheduledThreadPoolExecutor * @throws NullPointerException {@inheritDoc} */ public void execute(Runnable command) { - schedule(command, 0, TimeUnit.NANOSECONDS); + schedule(command, 0, NANOSECONDS); } // Override AbstractExecutorService methods @@ -598,7 +601,7 @@ public class ScheduledThreadPoolExecutor * @throws NullPointerException {@inheritDoc} */ public Future<?> submit(Runnable task) { - return schedule(task, 0, TimeUnit.NANOSECONDS); + return schedule(task, 0, NANOSECONDS); } /** @@ -606,8 +609,7 @@ public class ScheduledThreadPoolExecutor * @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 @@ public class ScheduledThreadPoolExecutor * @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 @@ public class ScheduledThreadPoolExecutor * @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 @@ public class ScheduledThreadPoolExecutor * from the queue * @see #setRemoveOnCancelPolicy * @since 1.7 + * @hide */ - /*public*/ boolean getRemoveOnCancelPolicy() { // android-changed + public boolean getRemoveOnCancelPolicy() { return removeOnCancel; } @@ -724,8 +728,6 @@ public class ScheduledThreadPoolExecutor * 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 @@ public class ScheduledThreadPoolExecutor * 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 @@ public class ScheduledThreadPoolExecutor */ 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 @@ public class ScheduledThreadPoolExecutor /** * 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 @@ public class ScheduledThreadPoolExecutor * 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 @@ public class ScheduledThreadPoolExecutor * 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 @@ public class ScheduledThreadPoolExecutor 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 @@ public class ScheduledThreadPoolExecutor return Integer.MAX_VALUE; } - public RunnableScheduledFuture peek() { + public RunnableScheduledFuture<?> peek() { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -975,7 +976,7 @@ public class ScheduledThreadPoolExecutor 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 @@ public class ScheduledThreadPoolExecutor * 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 @@ public class ScheduledThreadPoolExecutor 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 class ScheduledThreadPoolExecutor } } - 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 class ScheduledThreadPoolExecutor } } - 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 @@ public class ScheduledThreadPoolExecutor 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 @@ public class ScheduledThreadPoolExecutor } /** - * 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 @@ public class ScheduledThreadPoolExecutor 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 @@ public class ScheduledThreadPoolExecutor 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 @@ import java.util.concurrent.atomic.*; * <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 @@ import java.util.concurrent.atomic.*; * } * 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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } // 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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> */ 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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } // 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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } // 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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } } - // 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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> * * @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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> * @return a zero-length array */ public Object[] toArray() { - return EmptyArray.OBJECT; // android-changed + return new Object[0]; } /** @@ -1036,8 +1083,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> 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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> 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 @@ public class SynchronousQueue<E> extends AbstractQueue<E> 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 @@ package java.util.concurrent; * * <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.locks.*; 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 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * <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 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * 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 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** + * 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 @@ package java.util.concurrent; * 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 @@ import sun.misc.Unsafe; 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { } + /** + * 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 @@ import java.util.*; 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 @@ public abstract class AtomicIntegerFieldUpdater<T> { * 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 @@ public abstract class AtomicIntegerFieldUpdater<T> { 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 @@ public class AtomicLong extends Number implements java.io.Serializable { 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 @@ public class AtomicLong extends Number implements java.io.Serializable { 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 @@ public class AtomicLong extends Number implements java.io.Serializable { } + /** + * 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 @@ import java.util.*; 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 @@ public abstract class AtomicLongFieldUpdater<T> { } 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 @@ public abstract class AtomicLongFieldUpdater<T> { 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 @@ public abstract class AtomicLongFieldUpdater<T> { 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 @@ public abstract class AtomicLongFieldUpdater<T> { 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 63f46d6..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 @@ public class AtomicMarkableReference<V> { // 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 @@ import sun.misc.Unsafe; 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 @@ import java.util.*; 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 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { /** * 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 class AtomicReferenceArray<E> implements java.io.Serializable { */ 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 class AtomicReferenceArray<E> implements java.io.Serializable { 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 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { * @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 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { } } + /** + * 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 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { 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 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { 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 @@ public class AtomicStampedReference<V> { // 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/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 @@ public abstract class AbstractQueuedLongSynchronizer /** * Convenience method to interrupt current thread. */ - private static void selfInterrupt() { + static void selfInterrupt() { Thread.currentThread().interrupt(); } @@ -1231,7 +1230,7 @@ public abstract class AbstractQueuedLongSynchronizer * 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 @@ import sun.misc.Unsafe; * 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 @@ import sun.misc.Unsafe; * 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 @@ import sun.misc.Unsafe; * public void await() throws InterruptedException { * sync.acquireSharedInterruptibly(1); * } - * } - * </pre> + * }}</pre> * * @since 1.5 * @author Doug Lea @@ -800,7 +797,7 @@ public abstract class AbstractQueuedSynchronizer /** * Convenience method to interrupt current thread. */ - private static void selfInterrupt() { + static void selfInterrupt() { Thread.currentThread().interrupt(); } @@ -2251,9 +2248,7 @@ public abstract class AbstractQueuedSynchronizer * 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 @@ public interface Condition { /** * 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 @@ import java.util.concurrent.TimeUnit; * 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 @@ public interface Lock { * 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 @@ import sun.misc.Unsafe; * 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 @@ import sun.misc.Unsafe; * * <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 @@ public class LockSupport { 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 @@ public class LockSupport { * 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 @@ import java.util.concurrent.atomic.*; * 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 @@ import java.util.concurrent.atomic.*; * lock.unlock() * } * } - * } - * </pre> + * }}</pre> * * <p>In addition to implementing the {@link Lock} interface, this * class defines methods {@code isLocked} and @@ -354,8 +352,11 @@ public class ReentrantLock implements Lock, java.io.Serializable { * 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 @@ public class ReentrantLock implements Lock, java.io.Serializable { * 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 @@ public class ReentrantLock implements Lock, java.io.Serializable { * 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 @@ public class ReentrantLock implements Lock, java.io.Serializable { * 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 @@ public class ReentrantLock implements Lock, java.io.Serializable { * 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 @@ public class ReentrantLock implements Lock, java.io.Serializable { * 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 @@ import java.util.*; * <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 @@ import java.util.*; * 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 @@ import java.util.*; * 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 @@ import java.util.*; * 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 @@ public class ReentrantReadWriteLock } /** - * 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 @@ public class ReentrantReadWriteLock * 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 @@ public class ReentrantReadWriteLock * 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/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 @@ public class JarOutputStream extends ZipOutputStream { * * @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 @@ public class FileHandler extends StreamHandler { 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 abstract class Handler { */ 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 @@ public abstract class Handler { /** * 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 @@ public abstract class Handler { */ void internalSetFormatter(Formatter newFormatter) { if (newFormatter == null) { - throw new NullPointerException(); + throw new NullPointerException("newFormatter == null"); } this.formatter = newFormatter; } @@ -356,7 +356,7 @@ public abstract class Handler { */ 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 class LogManager { */ 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 @@ public class StreamHandler extends Handler { */ 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 @@ public abstract class AbstractPreferences extends Preferences { @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 @@ public abstract class AbstractPreferences extends Preferences { @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 @@ public abstract class AbstractPreferences extends Preferences { @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 @@ public abstract class AbstractPreferences extends Preferences { @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 @@ public abstract class AbstractPreferences extends Preferences { @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 @@ public abstract class AbstractPreferences extends Preferences { @Override public void addNodeChangeListener(NodeChangeListener ncl) { if (ncl == null) { - throw new NullPointerException(); + throw new NullPointerException("ncl == null"); } checkState(); synchronized (nodeChangeListeners) { @@ -741,7 +743,7 @@ public abstract class AbstractPreferences extends Preferences { @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 @@ public final class Matcher implements MatchResult { 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 @@ public final class Matcher implements MatchResult { resetForInput(); matchFound = false; - findPos = regionStart; appendPos = 0; return this; @@ -377,30 +371,17 @@ public final class Matcher implements MatchResult { } /** - * 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 final class Matcher implements MatchResult { */ public boolean find() { matchFound = findNextImpl(address, input, matchOffsets); - if (matchFound) { - findPos = matchOffsets[1]; - } return matchFound; } @@ -429,9 +407,6 @@ public final class Matcher implements MatchResult { */ public boolean lookingAt() { matchFound = lookingAtImpl(address, input, matchOffsets); - if (matchFound) { - findPos = matchOffsets[1]; - } return matchFound; } @@ -444,9 +419,6 @@ public final class Matcher implements MatchResult { */ 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 @@ import java.io.Serializable; * </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 class DeflaterInputStream extends FilterInputStream { */ 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 class DeflaterOutputStream extends FilterOutputStream { */ 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 class InflaterInputStream extends FilterInputStream { */ 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 class InflaterOutputStream extends FilterOutputStream { */ 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 988bd2c..e2bfc8d 100644 --- a/luni/src/main/java/java/util/zip/ZipEntry.java +++ b/luni/src/main/java/java/util/zip/ZipEntry.java @@ -71,7 +71,7 @@ public class ZipEntry implements ZipConstants, Cloneable { */ 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 363f57e..6ecd489 100644 --- a/luni/src/main/java/java/util/zip/ZipFile.java +++ b/luni/src/main/java/java/util/zip/ZipFile.java @@ -223,7 +223,7 @@ public class ZipFile implements ZipConstants { 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 d082fc7..e7c4566 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 class ZipInputStream extends InflaterInputStream implements ZipConstants 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 @@ public class Cipher { 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 class Cipher { 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 class Cipher { 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 @@ public class CipherInputStream extends FilterInputStream { @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 @@ public class EncryptedPrivateKeyInfo { * 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 @@ public class EncryptedPrivateKeyInfo { * @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 class ExemptionMechanism { 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 @@ public class ExemptionMechanism { throw new NoSuchProviderException(provider); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } return getInstance(algorithm, impProvider); } @@ -159,7 +159,7 @@ public class ExemptionMechanism { 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 class KeyAgreement { 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 @@ public class KeyAgreement { 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 class KeyGenerator { 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 @@ public class KeyGenerator { 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 class Mac implements Cloneable { 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 @@ public class Mac implements Cloneable { 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 @@ import java.security.NoSuchProviderException; /** * 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 @@ public class SealedObject implements Serializable { 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 5e47abe..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 class SecretKeyFactory { 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 @@ public class SecretKeyFactory { 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 class DESedeKeySpec implements KeySpec { */ 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 class DESedeKeySpec implements KeySpec { */ 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 class OAEPParameterSpec implements AlgorithmParameterSpec { */ 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 @@ public class PSource { */ protected PSource(String pSrcName) { if (pSrcName == null) { - throw new NullPointerException(); + throw new NullPointerException("pSrcName == null"); } this.pSrcName = pSrcName; } @@ -85,7 +85,7 @@ public class PSource { 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 class KeyManagerFactory { 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 @@ public class KeyManagerFactory { 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 class SSLContext { */ 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 @@ public class SSLContext { 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 class TrustManagerFactory { 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 @@ public class TrustManagerFactory { 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 final class Subject implements Serializable { 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 final class Subject implements Serializable { */ 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 @@ public final class Subject implements Serializable { 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 final class Subject implements Serializable { public boolean retainAll(Collection<?> c) { if (c == null) { - throw new NullPointerException(); + throw new NullPointerException("c == null"); } return super.retainAll(c); } @@ -624,7 +628,7 @@ public final class Subject implements Serializable { 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 final class Subject implements Serializable { 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 final class X500Principal implements Serializable, Principal { */ 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 final class X500Principal implements Serializable, Principal { 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 abstract class DatatypeFactory { */ 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 abstract class DatatypeFactory { */ 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 @@ public abstract class Duration { // 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 @@ package javax.xml.namespace; 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 abstract class SchemaFactory { */ 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 abstract class SchemaFactory { 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 abstract class SchemaFactory { */ 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 abstract class SchemaFactory { */ 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 abstract class SchemaFactory { */ 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 @@ final class SchemaFactoryFinder { * 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 @@ public abstract class Validator { * @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 @@ public abstract class Validator { * @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 @@ public abstract class Validator { * 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 @@ public abstract class Validator { * @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 @@ public abstract class ValidatorHandler implements ContentHandler { * @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 @@ public abstract class ValidatorHandler implements ContentHandler { * @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 @@ public abstract class ValidatorHandler implements ContentHandler { * 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 @@ public abstract class ValidatorHandler implements ContentHandler { * @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 class XPathException extends Exception { */ 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 class XPathException extends Exception { 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 abstract class XPathFactory { 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 abstract class XPathFactory { 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 @@ final class XPathFactoryFinder { * 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 @@ or more nodes from an XML document:</p> 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 final class ICU { 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.text.DateFormat; 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 final class LocaleData { public Integer minimalDaysInFirstWeek; // Used by DateFormatSymbols. - public String[] amPm; - public String[] eras; - - public String[] longMonthNames; - public String[] shortMonthNames; - public String[] longStandAloneMonthNames; - public String[] shortStandAloneMonthNames; - - public String[] longWeekdayNames; - public String[] shortWeekdayNames; - public String[] longStandAloneWeekdayNames; - public String[] shortStandAloneWeekdayNames; + public String[] amPm; // "AM", "PM". + public String[] eras; // "BC", "AD". + + 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; // "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 @@ public final class LocaleData { } @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 final class NativeConverter { } } - 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/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 @@ public final class NativeIDN { 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 final class RuleBasedCollatorICU implements Cloneable { 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..c61a3cf 100644 --- a/luni/src/main/java/libcore/io/BlockGuardOs.java +++ b/luni/src/main/java/libcore/io/BlockGuardOs.java @@ -50,7 +50,7 @@ public class BlockGuardOs extends ForwardingOs { } } - @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 @@ public class BlockGuardOs extends ForwardingOs { 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 @@ public class BlockGuardOs extends ForwardingOs { 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(); @@ -181,6 +181,12 @@ public class BlockGuardOs extends ForwardingOs { return tagSocket(os.socket(domain, type, protocol)); } + @Override public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { + os.socketpair(domain, type, protocol, fd1, fd2); + tagSocket(fd1); + tagSocket(fd2); + } + @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { BlockGuard.getThreadPolicy().onWriteToDisk(); return os.write(fd, buffer); diff --git a/luni/src/main/java/libcore/io/DiskLruCache.java b/luni/src/main/java/libcore/io/DiskLruCache.java index fa26624..8338983 100644 --- a/luni/src/main/java/libcore/io/DiskLruCache.java +++ b/luni/src/main/java/libcore/io/DiskLruCache.java @@ -458,9 +458,14 @@ public final class DiskLruCache implements Closeable { // 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 DiskLruCache implements Closeable { */ 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 @@ public final class DiskLruCache implements Closeable { 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/DropBox.java b/luni/src/main/java/libcore/io/DropBox.java new file mode 100644 index 0000000..cf88106 --- /dev/null +++ b/luni/src/main/java/libcore/io/DropBox.java @@ -0,0 +1,72 @@ +/* + * 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; + +public final class DropBox { + + /** + * Hook for customizing how events are reported. + */ + private static volatile Reporter REPORTER = new DefaultReporter(); + + /** + * Used to replace default Reporter for logging events. Must be non-null. + */ + public static void setReporter(Reporter reporter) { + if (reporter == null) { + throw new NullPointerException("reporter == null"); + } + REPORTER = reporter; + } + + /** + * Returns non-null Reporter. + */ + public static Reporter getReporter() { + return REPORTER; + } + + /** + * Interface to allow customization of reporting behavior. + */ + public static interface Reporter { + public void addData(String tag, byte[] data, int flags); + public void addText(String tag, String data); + } + + /** + * Default Reporter which reports events to the log. + */ + private static final class DefaultReporter implements Reporter { + + public void addData(String tag, byte[] data, int flags) { + System.out.println(tag + ": " + Base64.encode(data)); + } + + public void addText(String tag, String data) { + System.out.println(tag + ": " + data); + } + } + + public static void addData(String tag, byte[] data, int flags) { + getReporter().addData(tag, data, flags); + } + + public static void addText(String tag, String data) { + getReporter().addText(tag, data); + } +} diff --git a/luni/src/main/java/libcore/io/EventLogger.java b/luni/src/main/java/libcore/io/EventLogger.java new file mode 100644 index 0000000..9709cc9 --- /dev/null +++ b/luni/src/main/java/libcore/io/EventLogger.java @@ -0,0 +1,69 @@ +/* + * 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; + +public final class EventLogger { + + /** + * Hook for customizing how events are reported. + */ + private static volatile Reporter REPORTER = new DefaultReporter(); + + /** + * Used to replace default Reporter for logging events. Must be non-null. + */ + public static void setReporter(Reporter reporter) { + if (reporter == null) { + throw new NullPointerException("reporter == null"); + } + REPORTER = reporter; + } + + /** + * Returns non-null Reporter. + */ + public static Reporter getReporter() { + return REPORTER; + } + + /** + * Interface to allow customization of reporting behavior. + */ + public static interface Reporter { + public void report (int code, Object... list); + } + + /** + * Default Reporter which reports events to the log. + */ + private static final class DefaultReporter implements Reporter { + @Override + public void report (int code, Object... list) { + StringBuilder sb = new StringBuilder(); + sb.append(code); + for (Object o : list) { + sb.append(","); + sb.append(o.toString()); + } + System.out.println(sb); + } + } + + public static void writeEvent(int code, Object... list) { + getReporter().report(code, list); + } +} diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java index 2c8e562..ee26d04 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.io.FileDescriptor; 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 @@ public class ForwardingOs implements Os { 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 class ForwardingOs implements Os { 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 class ForwardingOs implements Os { 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); } @@ -115,11 +121,14 @@ public class ForwardingOs implements Os { public void setuid(int uid) throws ErrnoException { os.setuid(uid); } public void shutdown(FileDescriptor fd, int how) throws ErrnoException { os.shutdown(fd, how); } public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { return os.socket(domain, type, protocol); } + public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { os.socketpair(domain, type, protocol, fd1, fd2); } public StructStat stat(String path) throws ErrnoException { return os.stat(path); } public StructStatFs statfs(String path) throws ErrnoException { return os.statfs(path); } 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..1f42497 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.io.FileDescriptor; 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 interface Os { 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 interface Os { 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; @@ -108,12 +114,15 @@ public interface Os { public void setuid(int uid) throws ErrnoException; public void shutdown(FileDescriptor fd, int how) throws ErrnoException; public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; + public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException; public StructStat stat(String path) throws ErrnoException; /* TODO: replace statfs with statvfs. */ public StructStatFs statfs(String path) throws ErrnoException; 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 final class OsConstants { 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..2fb187e 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.io.FileDescriptor; 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 @@ import libcore.util.MutableLong; 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 final class Posix implements Os { 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 @@ public final class Posix implements Os { } 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; @@ -159,11 +165,14 @@ public final class Posix implements Os { public native void setuid(int uid) throws ErrnoException; public native void shutdown(FileDescriptor fd, int how) throws ErrnoException; public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; + public native void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException; public native StructStat stat(String path) throws ErrnoException; public native StructStatFs statfs(String path) throws ErrnoException; 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 index 5f8d452..36556a0 100644 --- a/luni/src/main/java/libcore/io/StrictLineReader.java +++ b/luni/src/main/java/libcore/io/StrictLineReader.java @@ -106,8 +106,10 @@ public class StrictLineReader implements Closeable { * or the specified charset is not supported. */ public StrictLineReader(InputStream in, int capacity, Charset charset) { - if (in == null || charset == null) { - throw new NullPointerException(); + 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"); 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 @@ public final class MimeUtils { 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 @@ public abstract class UriCodec { 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/HttpConnection.java b/luni/src/main/java/libcore/net/http/HttpConnection.java index 2f92706..4a6e65d 100644 --- a/luni/src/main/java/libcore/net/http/HttpConnection.java +++ b/luni/src/main/java/libcore/net/http/HttpConnection.java @@ -199,7 +199,6 @@ final class HttpConnection { // tlsTolerant mimics Chrome's behavior if (tlsTolerant && unverifiedSocket instanceof OpenSSLSocketImpl) { OpenSSLSocketImpl openSslSocket = (OpenSSLSocketImpl) unverifiedSocket; - openSslSocket.setEnabledCompressionMethods(new String[] { "ZLIB"}); openSslSocket.setUseSessionTickets(true); openSslSocket.setHostname(address.uriHost); // use SSLSocketFactory default enabled protocols diff --git a/luni/src/main/java/libcore/net/http/HttpEngine.java b/luni/src/main/java/libcore/net/http/HttpEngine.java index 3e4b9d3..42c9b96 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 @@ import libcore.util.EmptyArray; * 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 @@ public class HttpEngine { /* * 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()); } @@ -490,8 +491,15 @@ public class HttpEngine { reusable = false; } - // If the headers specify that the connection shouldn't be reused, don't reuse it. - if (hasConnectionCloseHeader()) { + // If the request specified that the connection shouldn't be reused, + // don't reuse it. This advice doesn't apply to CONNECT requests because + // the "Connection: close" header goes the origin server, not the proxy. + if (requestHeaders.hasConnectionClose() && method != CONNECT) { + reusable = false; + } + + // If the response specified that the connection shouldn't be reused, don't reuse it. + if (responseHeaders != null && responseHeaders.hasConnectionClose()) { reusable = false; } @@ -523,8 +531,13 @@ public class HttpEngine { /* * If the response was transparently gzipped, remove the gzip header field * so clients don't double decompress. http://b/3009828 + * + * Also remove the Content-Length in this case because it contains the length + * of the gzipped response. This isn't terribly useful and is dangerous because + * clients can query the content length, but not the content encoding. */ responseHeaders.stripContentEncoding(); + responseHeaders.stripContentLength(); responseBodyIn = new GZIPInputStream(transferStream); } else { responseBodyIn = transferStream; @@ -758,11 +771,6 @@ public class HttpEngine { return agent != null ? agent : ("Java" + System.getProperty("java.version")); } - private boolean hasConnectionCloseHeader() { - return (responseHeaders != null && responseHeaders.hasConnectionClose()) - || requestHeaders.hasConnectionClose(); - } - protected final String getOriginAddress(URL url) { int port = url.getPort(); String result = url.getHost(); diff --git a/luni/src/main/java/libcore/net/http/HttpResponseCache.java b/luni/src/main/java/libcore/net/http/HttpResponseCache.java index db036b6..1a9dfd1 100644 --- a/luni/src/main/java/libcore/net/http/HttpResponseCache.java +++ b/luni/src/main/java/libcore/net/http/HttpResponseCache.java @@ -426,7 +426,7 @@ public final class HttpResponseCache extends ResponseCache implements ExtendedRe } 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'); 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.security.Permission; 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 @@ class HttpURLConnectionImpl extends HttpURLConnection { @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/net/http/ResponseHeaders.java b/luni/src/main/java/libcore/net/http/ResponseHeaders.java index 003b445..c0b4200 100644 --- a/luni/src/main/java/libcore/net/http/ResponseHeaders.java +++ b/luni/src/main/java/libcore/net/http/ResponseHeaders.java @@ -187,6 +187,11 @@ public final class ResponseHeaders { headers.removeAll("Content-Encoding"); } + public void stripContentLength() { + contentLength = -1; + headers.removeAll("Content-Length"); + } + public boolean isChunked() { return "chunked".equalsIgnoreCase(transferEncoding); } 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 class BasicLruCache<K, V> { */ 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 @@ public class BasicLruCache<K, V> { * 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 final class Objects { 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 @@ public final class ZoneInfo extends TimeZone { 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 @@ public final class ZoneInfo extends TimeZone { } mUseDst = usesDst; + // tzdata uses seconds, but Java uses milliseconds. mRawOffset *= 1000; mEarliestRawOffset = earliestRawOffset * 1000; } @@ -185,6 +203,10 @@ public final class ZoneInfo extends TimeZone { mRawOffset = off; } + @Override public int getDSTSavings() { + return mUseDst ? mDstSavings: 0; + } + @Override public boolean useDaylightTime() { return mUseDst; } @@ -235,7 +257,7 @@ public final class ZoneInfo extends TimeZone { 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 @@ public final class ZoneInfoDB { } } - 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 final class ZoneInfoDB { } } - 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 @@ public class NullCipherSpi extends CipherSpi { @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 class SystemScope extends IdentityScope { */ 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 @@ public class Engine { /** 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 @@ public class Engine { 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 @@ public class Engine { 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 @@ import java.util.Map; /** * 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; + /** + * Save default SecureRandom service as well. + * Avoids similar provider/services iteration in SecureRandom constructor. + */ + private static Provider.Service cachedSecureRandomService; + + /** + * Need refresh flag. + */ + private static boolean needRefresh; /** - * Refresh number + * The cacheVersion is changed on every update of service + * information. It is used by external callers to validate their + * own caches of Service information. */ - static int refreshNumber = 1; + private static int cacheVersion = 1; - // Registered providers + /** + * 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 @@ public class Services { } /** - * 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 @@ public class Services { } /** - * 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 - * - */ - 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 + * Returns true if services contain any provider information. */ - 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 class X509CRLImpl extends X509CRL { */ 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 @@ public class SHA1withDSA_SignatureImpl extends Signature { 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 @@ public final class AuthorityKeyIdentifier extends ExtensionValue { 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 @@ public final class AuthorityKeyIdentifier extends ExtensionValue { @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 @@ public final class SubjectKeyIdentifier extends ExtensionValue { 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 @@ abstract class ExpatAttributes implements Attributes { 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 @@ abstract class ExpatAttributes implements Attributes { 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 @@ abstract class ExpatAttributes implements Attributes { 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 @@ abstract class ExpatAttributes implements Attributes { 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 @@ abstract class ExpatAttributes implements Attributes { 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 abstract class NodeImpl implements Node { 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 abstract class NodeImpl implements Node { 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 @@ public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory { @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 class DocumentBuilderFactoryImpl extends DocumentBuilderFactory { 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 @@ public class SAXParserFactoryImpl extends SAXParserFactory { @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 @@ public class SAXParserFactoryImpl extends SAXParserFactory { @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/CertPinManager.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java new file mode 100644 index 0000000..4cdd8d2 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java @@ -0,0 +1,184 @@ +/* + * 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.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.net.ssl.DefaultHostnameVerifier; +import libcore.io.IoUtils; +import libcore.util.BasicLruCache; + +/** + * This class provides a simple interface for cert pinning. + */ +public class CertPinManager { + + private long lastModified; + + private final Map<String, PinListEntry> entries = new HashMap<String, PinListEntry>(); + private final BasicLruCache<String, String> hostnameCache = new BasicLruCache<String, String>(10); + private final DefaultHostnameVerifier verifier = new DefaultHostnameVerifier(); + + private boolean initialized = false; + private static final boolean DEBUG = false; + + private final File pinFile; + private final TrustedCertificateStore certStore; + + public CertPinManager(TrustedCertificateStore store) throws PinManagerException { + pinFile = new File("/data/misc/keychain/pins"); + certStore = store; + rebuild(); + } + + /** Test only */ + public CertPinManager(String path, TrustedCertificateStore store) throws PinManagerException { + if (path == null) { + throw new NullPointerException("path == null"); + } + pinFile = new File(path); + certStore = store; + rebuild(); + } + + /** + * This is the public interface for cert pinning. + * + * Given a hostname and a certificate chain this verifies that the chain includes + * certs from the pinned list provided. + * + * If the chain doesn't include those certs and is in enforcing mode, then this method + * returns true and the certificate check should fail. + */ + public boolean chainIsNotPinned(String hostname, List<X509Certificate> chain) + throws PinManagerException { + // lookup the entry + PinListEntry entry = lookup(hostname); + + // return its result or false if there's no pin + if (entry != null) { + return entry.chainIsNotPinned(chain); + } + return false; + } + + private synchronized void rebuild() throws PinManagerException { + // reread the pin file + String pinFileContents = readPinFile(); + + if (pinFileContents != null) { + // rebuild the pinned certs + for (String entry : getPinFileEntries(pinFileContents)) { + try { + PinListEntry pin = new PinListEntry(entry, certStore); + entries.put(pin.getCommonName(), pin); + } catch (PinEntryException e) { + log("Pinlist contains a malformed pin: " + entry, e); + } + } + + // clear the cache + hostnameCache.evictAll(); + + // set the last modified time + lastModified = pinFile.lastModified(); + + // we've been fully initialized and are ready to go + initialized = true; + } + } + + private String readPinFile() throws PinManagerException { + try { + return IoUtils.readFileAsString(pinFile.getPath()); + } catch (FileNotFoundException e) { + // there's no pin list, all certs are unpinned + return null; + } catch (IOException e) { + // this is unexpected, fail + throw new PinManagerException("Unexpected error reading pin list; failing.", e); + } + } + + private static String[] getPinFileEntries(String pinFileContents) { + return pinFileContents.split("\n"); + } + + private synchronized PinListEntry lookup(String hostname) throws PinManagerException { + + // if we don't have any data, don't bother + if (!initialized) { + return null; + } + + // check to see if our cache is valid + if (cacheIsNotValid()) { + rebuild(); + } + + // if so, check the hostname cache + String cn = hostnameCache.get(hostname); + if (cn != null) { + // if we hit, return the corresponding entry + return entries.get(cn); + } + + // otherwise, get the matching cn + cn = getMatchingCN(hostname); + if (cn != null) { + hostnameCache.put(hostname, cn); + // we have a matching CN, return that entry + return entries.get(cn); + } + + // if we got here, we don't have a matching CN for this hostname + return null; + } + + private boolean cacheIsNotValid() { + return pinFile.lastModified() != lastModified; + } + + private String getMatchingCN(String hostname) { + String bestMatch = ""; + for (String cn : entries.keySet()) { + // skip shorter CNs since they can't be better matches + if (cn.length() < bestMatch.length()) { + continue; + } + // now verify that the CN matches at all + if (verifier.verifyHostName(hostname, cn)) { + bestMatch = cn; + } + } + return bestMatch; + } + + private static void log(String s, Exception e) { + if (DEBUG) { + System.out.println("PINFILE: " + s); + if (e != null) { + e.printStackTrace(); + } + } + } +} 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 4b29363..c855c0c 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; @@ -39,6 +37,7 @@ import javax.crypto.spec.DHPublicKeySpec; import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; /** @@ -90,7 +89,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { if (engineOwner != null) { session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort()); } else { - session.setPeer(socketOwner.getInetAddress().getHostName(), socketOwner.getPort()); + session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort()); } session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols()); recordProtocol.setVersion(session.protocol.version); @@ -111,7 +110,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { if (engineOwner != null) { session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort()); } else { - session.setPeer(socketOwner.getInetAddress().getHostName(), socketOwner.getPort()); + session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort()); } session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols()); recordProtocol.setVersion(session.protocol.version); @@ -500,7 +499,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { // 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); @@ -529,8 +528,21 @@ public class ClientHandshakeImpl extends HandshakeProtocol { if (authType == null) { return; } + String hostname = null; + if (engineOwner != null) { + hostname = engineOwner.getPeerHost(); + } else { + // we don't want to do an inet address lookup here in case we're talking to a proxy + hostname = socketOwner.getWrappedHostName(); + } try { - parameters.getTrustManager().checkServerTrusted(serverCert.certs, authType); + X509TrustManager x509tm = parameters.getTrustManager(); + if (x509tm instanceof TrustManagerImpl) { + TrustManagerImpl tm = (TrustManagerImpl) x509tm; + tm.checkServerTrusted(serverCert.certs, authType, hostname); + } else { + x509tm.checkServerTrusted(serverCert.certs, authType); + } } catch (CertificateException e) { fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e); return; @@ -561,8 +573,8 @@ public class ClientHandshakeImpl extends HandshakeProtocol { host = engineOwner.getPeerHost(); port = engineOwner.getPeerPort(); } else { - host = socketOwner.getInetAddress().getHostName(); - port = socketOwner.getPort(); + host = socketOwner.getPeerHostName(); + port = socketOwner.getPeerPort(); } if (host == null || port == -1) { return null; // starts new session 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 @@ public class FileClientSessionCache { */ 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 @@ public class FileClientSessionCache { 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 @@ package org.apache.harmony.xnet.provider.jsse; 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 759fc85..65373ff 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.net.SocketTimeoutException; 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.HashMap; 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 final class NativeCrypto { 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); @@ -74,6 +78,8 @@ public final class NativeCrypto { public static native void EVP_PKEY_free(int pkey); + public static native int EVP_PKEY_cmp(int pkey1, int pkey2); + public static native byte[] i2d_PKCS8_PRIV_KEY_INFO(int pkey); public static native int d2i_PKCS8_PRIV_KEY_INFO(byte[] data); @@ -84,6 +90,20 @@ public final class NativeCrypto { 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 +192,8 @@ public final class NativeCrypto { 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,13 +355,16 @@ public final class NativeCrypto { 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; // SSL options from ssl.h public static final long SSL_OP_NO_TICKET = 0x00004000L; public static final long SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000L; - public static final long SSL_OP_NO_COMPRESSION = 0x00020000L; public static final long SSL_OP_NO_SSLv3 = 0x02000000L; public static final long SSL_OP_NO_TLSv1 = 0x04000000L; public static final long SSL_OP_NO_TLSv1_1 = 0x10000000L; @@ -544,66 +569,6 @@ public final class NativeCrypto { return cipherSuites; } - public static final String SUPPORTED_COMPRESSION_METHOD_ZLIB = "ZLIB"; - public static final String SUPPORTED_COMPRESSION_METHOD_NULL = "NULL"; - - private static final String[] SUPPORTED_COMPRESSION_METHODS - = { SUPPORTED_COMPRESSION_METHOD_ZLIB, SUPPORTED_COMPRESSION_METHOD_NULL }; - - public static String[] getSupportedCompressionMethods() { - return SUPPORTED_COMPRESSION_METHODS.clone(); - } - - public static final String[] getDefaultCompressionMethods() { - return new String[] { SUPPORTED_COMPRESSION_METHOD_NULL }; - } - - public static String[] checkEnabledCompressionMethods(String[] methods) { - if (methods == null) { - throw new IllegalArgumentException("methods == null"); - } - if (methods.length < 1 - && !methods[methods.length-1].equals(SUPPORTED_COMPRESSION_METHOD_NULL)) { - throw new IllegalArgumentException("last method must be NULL"); - } - for (int i = 0; i < methods.length; i++) { - String method = methods[i]; - if (method == null) { - throw new IllegalArgumentException("methods[" + i + "] == null"); - } - if (!method.equals(SUPPORTED_COMPRESSION_METHOD_ZLIB) - && !method.equals(SUPPORTED_COMPRESSION_METHOD_NULL)) { - throw new IllegalArgumentException("method " + method - + " is not supported"); - } - } - return methods; - } - - public static void setEnabledCompressionMethods(int ssl, String[] methods) { - checkEnabledCompressionMethods(methods); - // openssl uses negative logic letting you disable compression. - // so first, assume we need to set all (disable all) and clear none (enable none). - // in the loop, selectively move bits from set to clear (from disable to enable) - long optionsToSet = (SSL_OP_NO_COMPRESSION); - long optionsToClear = 0; - for (int i = 0; i < methods.length; i++) { - String method = methods[i]; - if (method.equals(SUPPORTED_COMPRESSION_METHOD_NULL)) { - // nothing to do to support NULL - } else if (method.equals(SUPPORTED_COMPRESSION_METHOD_ZLIB)) { - optionsToSet &= ~SSL_OP_NO_COMPRESSION; - optionsToClear |= SSL_OP_NO_COMPRESSION; - } else { - // error checked by checkEnabledCompressionMethods - throw new IllegalStateException(); - } - } - - SSL_set_options(ssl, optionsToSet); - SSL_clear_options(ssl, optionsToClear); - } - /* * See the OpenSSL ssl.h header file for more information. */ @@ -680,7 +645,7 @@ public final class NativeCrypto { public static native int SSL_read(int sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc, - byte[] b, int off, int len, int timeoutMillis) + byte[] b, int off, int len, int readTimeoutMillis) throws IOException; /** @@ -689,7 +654,7 @@ public final class NativeCrypto { public static native void SSL_write(int sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc, - byte[] b, int off, int len) + byte[] b, int off, int len, int writeTimeoutMillis) throws IOException; public static native void SSL_interrupt(int sslNativePointer); @@ -707,9 +672,6 @@ public final class NativeCrypto { public static native String SSL_SESSION_cipher(int sslSessionNativePointer); - public static native String SSL_SESSION_compress_meth(int sslCtxNativePointer, - int sslSessionNativePointer); - public static native void SSL_SESSION_free(int sslSessionNativePointer); public static native byte[] i2d_SSL_SESSION(int sslSessionNativePointer); 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..ddf2e0d --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java @@ -0,0 +1,356 @@ +/* + * 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() { + if (encrypting) { + return paddedBlockSizeBytes(); + } + return keySizeBytes(); + } + + @Override + protected int engineGetOutputSize(int inputLen) { + if (encrypting) { + return keySizeBytes(); + } + return paddedBlockSizeBytes(); + } + + private int paddedBlockSizeBytes() { + int paddedBlockSizeBytes = keySizeBytes(); + if (padding == NativeCrypto.RSA_PKCS1_PADDING) { + paddedBlockSizeBytes--; // for 0 prefix + paddedBlockSizeBytes -= 10; // PKCS1 padding header length + } + return paddedBlockSizeBytes; + } + + private int keySizeBytes() { + if (key == null) { + throw new IllegalStateException("cipher is not initialized"); + } + return NativeCrypto.RSA_size(this.key.getPkeyContext()); + } + + @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 @@ public class OpenSSLDSAPrivateKey implements DSAPrivateKey { 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 @@ public class OpenSSLEngine { NativeCrypto.ENGINE_load_dynamic(); } + private static final Object mLoadingLock = new Object(); + /** The ENGINE's native handle. */ private final int ctx; @@ -32,10 +34,14 @@ public class OpenSSLEngine { 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 @@ public class OpenSSLEngine { ctx = engineCtx; if (NativeCrypto.ENGINE_init(engineCtx) == 0) { + NativeCrypto.ENGINE_free(engineCtx); throw new IllegalArgumentException("Could not initialize engine"); } } @@ -62,9 +69,9 @@ public class OpenSSLEngine { 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 @@ class OpenSSLKey { 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 @@ class OpenSSLKey { 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..d4aa57f 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 @@ -73,33 +73,32 @@ public final class OpenSSLProvider extends Provider { // put("KeyFactory.DSA", OpenSSLDSAKeyFactory.class.getName()); // Signatures - put("Signature.MD5WithRSAEncryption", OpenSSLSignature.MD5RSA.class.getName()); - put("Alg.Alias.Signature.MD5WithRSA", "MD5WithRSAEncryption"); - put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", - "MD5WithRSAEncryption"); - - put("Signature.SHA1WithRSAEncryption", OpenSSLSignature.SHA1RSA.class.getName()); - put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSAEncryption"); - - put("Signature.SHA256WithRSAEncryption", OpenSSLSignature.SHA256RSA.class.getName()); - put("Alg.Alias.Signature.SHA256WithRSA", "SHA256WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSAEncryption"); - - put("Signature.SHA384WithRSAEncryption", OpenSSLSignature.SHA384RSA.class.getName()); - put("Alg.Alias.Signature.SHA384WithRSA", "SHA384WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSAEncryption"); - - put("Signature.SHA512WithRSAEncryption", OpenSSLSignature.SHA512RSA.class.getName()); - put("Alg.Alias.Signature.SHA512WithRSA", "SHA512WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSAEncryption"); + put("Signature.MD5WithRSA", OpenSSLSignature.MD5RSA.class.getName()); + put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5WithRSA"); + put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5WithRSA"); + + put("Signature.SHA1WithRSA", OpenSSLSignature.SHA1RSA.class.getName()); + put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSA"); + put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSA"); + put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSA"); + put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSA"); + put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSA"); + put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSA"); + + put("Signature.SHA256WithRSA", OpenSSLSignature.SHA256RSA.class.getName()); + put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSA"); + + put("Signature.SHA384WithRSA", OpenSSLSignature.SHA384RSA.class.getName()); + put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSA"); + + put("Signature.SHA512WithRSA", OpenSSLSignature.SHA512RSA.class.getName()); + put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSA"); put("Signature.SHA1withDSA", OpenSSLSignature.SHA1DSA.class.getName()); put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA"); @@ -108,5 +107,22 @@ public final class OpenSSLProvider extends Provider { 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..4303e5a 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 @@ -191,8 +191,8 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA return true; } - if (o instanceof OpenSSLRSAPrivateCrtKey) { - OpenSSLRSAPrivateCrtKey other = (OpenSSLRSAPrivateCrtKey) o; + if (o instanceof OpenSSLRSAPrivateKey) { + OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o; /* * We can shortcut the true case, but it still may be equivalent but @@ -201,19 +201,36 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA if (getOpenSSLKey().equals(other.getOpenSSLKey())) { return true; } + + return NativeCrypto.EVP_PKEY_cmp(getPkeyContext(), other.getPkeyContext()) == 1; } if (o instanceof RSAPrivateCrtKey) { ensureReadParams(); RSAPrivateCrtKey other = (RSAPrivateCrtKey) o; - return getModulus().equals(other.getModulus()) - && publicExponent.equals(other.getPublicExponent()) - && getPrivateExponent().equals(other.getPrivateExponent()) - && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ()) - && primeExponentP.equals(other.getPrimeExponentP()) - && primeExponentQ.equals(other.getPrimeExponentQ()) - && crtCoefficient.equals(other.getCrtCoefficient()); + if (getOpenSSLKey().isEngineBased()) { + return getModulus().equals(other.getModulus()) + && publicExponent.equals(other.getPublicExponent()); + } else { + return getModulus().equals(other.getModulus()) + && publicExponent.equals(other.getPublicExponent()) + && getPrivateExponent().equals(other.getPrivateExponent()) + && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ()) + && primeExponentP.equals(other.getPrimeExponentP()) + && primeExponentQ.equals(other.getPrimeExponentQ()) + && crtCoefficient.equals(other.getCrtCoefficient()); + } + } else if (o instanceof RSAPrivateKey) { + ensureReadParams(); + RSAPrivateKey other = (RSAPrivateKey) o; + + if (getOpenSSLKey().isEngineBased()) { + return getModulus().equals(other.getModulus()); + } else { + return getModulus().equals(other.getModulus()) + && getPrivateExponent().equals(other.getPrivateExponent()); + } } return false; @@ -232,11 +249,11 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA 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 +267,11 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA 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..adb05a9 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 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey { return key.getPkeyContext(); } + public String getPkeyAlias() { + return key.getAlias(); + } + @Override public boolean equals(Object o) { if (o == this) { @@ -196,6 +200,8 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey { if (key.equals(other.getOpenSSLKey())) { return true; } + + return NativeCrypto.EVP_PKEY_cmp(getPkeyContext(), other.getPkeyContext()) == 1; } if (o instanceof RSAPrivateKey) { @@ -226,11 +232,11 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey { 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 +244,11 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey { 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/OpenSSLServerSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java index 841c31c..2f5fe59 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java @@ -32,7 +32,6 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { private final SSLParametersImpl sslParameters; private String[] enabledProtocols = NativeCrypto.getSupportedProtocols(); private String[] enabledCipherSuites = NativeCrypto.getDefaultCipherSuites(); - private String[] enabledCompressionMethods = NativeCrypto.getDefaultCompressionMethods(); protected OpenSSLServerSocketImpl(SSLParametersImpl sslParameters) throws IOException { this.sslParameters = sslParameters; @@ -126,26 +125,6 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites); } - public String[] getSupportedCompressionMethods() { - return NativeCrypto.getSupportedCompressionMethods(); - } - - public String[] getEnabledCompressionMethods() { - return enabledCompressionMethods.clone(); - } - - /** - * This method enables the compression methods listed by - * getSupportedCompressionMethods(). - * - * @param suites the names of all the compression methods to enable - * @throws IllegalArgumentException when one or more of the ciphers in array - * suites are not supported, or when the array is null. - */ - public void setEnabledCompressionMethods(String[] methods) { - enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods); - } - @Override public boolean getWantClientAuth() { return sslParameters.getWantClientAuth(); @@ -185,8 +164,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters, enabledProtocols.clone(), - enabledCipherSuites.clone(), - enabledCompressionMethods.clone()); + enabledCipherSuites.clone()); implAccept(socket); return socket; } 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 e194a38..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 @@ -49,7 +49,6 @@ public class OpenSSLSessionImpl implements SSLSession { private int peerPort = -1; private String cipherSuite; private String protocol; - private String compressionMethod; private AbstractSessionContext sessionContext; private byte[] id; @@ -328,19 +327,6 @@ public class OpenSSLSessionImpl implements SSLSession { } /** - * Returns the compression method name used in all connections - * pertaining to this SSL session. - */ - public String getCompressionMethod() { - if (compressionMethod == null) { - compressionMethod - = NativeCrypto.SSL_SESSION_compress_meth(sessionContext.sslCtxNativePointer, - sslSessionNativePointer); - } - return compressionMethod; - } - - /** * Returns the context to which the actual SSL session is bound. A SSL * context consists of (1) a possible delegate, (2) a provider and (3) a * protocol. @@ -380,9 +366,7 @@ public class OpenSSLSessionImpl implements SSLSession { /** * 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 @@ -398,9 +382,7 @@ public class OpenSSLSessionImpl implements SSLSession { /** * 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. @@ -413,9 +395,7 @@ public class OpenSSLSessionImpl implements SSLSession { * 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!) @@ -446,10 +426,6 @@ public class OpenSSLSessionImpl implements SSLSession { * <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 4c92952..4cc16e6 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 @@ -42,7 +42,11 @@ import javax.net.ssl.SSLProtocolException; import javax.net.ssl.SSLSession; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; +import static libcore.io.OsConstants.*; +import libcore.io.ErrnoException; +import libcore.io.Libcore; import libcore.io.Streams; +import libcore.io.StructTimeval; import org.apache.harmony.security.provider.cert.X509CertImpl; /** @@ -51,7 +55,6 @@ import org.apache.harmony.security.provider.cert.X509CertImpl; * Extensions to SSLSocket include: * <ul> * <li>handshake timeout - * <li>compression methods * <li>session tickets * <li>Server Name Indication * </ul> @@ -70,7 +73,6 @@ public class OpenSSLSocketImpl private byte[] npnProtocols; private String[] enabledProtocols; private String[] enabledCipherSuites; - private String[] enabledCompressionMethods; private boolean useSessionTickets; private String hostname; private OpenSSLSessionImpl sslSession; @@ -95,7 +97,8 @@ public class OpenSSLSocketImpl * OpenSSLSocketImplWrapper overrides setSoTimeout and * getSoTimeout to delegate to the wrapped socket. */ - private int timeoutMilliseconds = 0; + private int readTimeoutMilliseconds = 0; + private int writeTimeoutMilliseconds = 0; private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite private String wrappedHost; @@ -108,10 +111,9 @@ public class OpenSSLSocketImpl protected OpenSSLSocketImpl(SSLParametersImpl sslParameters, String[] enabledProtocols, - String[] enabledCipherSuites, - String[] enabledCompressionMethods) throws IOException { + String[] enabledCipherSuites) throws IOException { this.socket = this; - init(sslParameters, enabledProtocols, enabledCipherSuites, enabledCompressionMethods); + init(sslParameters, enabledProtocols, enabledCipherSuites); } protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters) @@ -169,8 +171,7 @@ public class OpenSSLSocketImpl private void init(SSLParametersImpl sslParameters) throws IOException { init(sslParameters, NativeCrypto.getDefaultProtocols(), - NativeCrypto.getDefaultCipherSuites(), - NativeCrypto.getDefaultCompressionMethods()); + NativeCrypto.getDefaultCipherSuites()); } /** @@ -179,12 +180,10 @@ public class OpenSSLSocketImpl */ private void init(SSLParametersImpl sslParameters, String[] enabledProtocols, - String[] enabledCipherSuites, - String[] enabledCompressionMethods) throws IOException { + String[] enabledCipherSuites) throws IOException { this.sslParameters = sslParameters; this.enabledProtocols = enabledProtocols; this.enabledCipherSuites = enabledCipherSuites; - this.enabledCompressionMethods = enabledCompressionMethods; } /** @@ -225,20 +224,6 @@ public class OpenSSLSocketImpl return null; } - String compressionMethod = session.getCompressionMethod(); - if (!compressionMethod.equals(NativeCrypto.SUPPORTED_COMPRESSION_METHOD_NULL)) { - boolean compressionMethodFound = false; - for (String enabledCompressionMethod : enabledCompressionMethods) { - if (compressionMethod.equals(enabledCompressionMethod)) { - compressionMethodFound = true; - break; - } - } - if (!compressionMethodFound) { - return null; - } - } - return session; } @@ -316,10 +301,6 @@ public class OpenSSLSocketImpl NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols); NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites); - if (enabledCompressionMethods.length != 0) { - NativeCrypto.setEnabledCompressionMethods(sslNativePointer, - enabledCompressionMethods); - } if (useSessionTickets) { NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET); } @@ -385,9 +366,11 @@ public class OpenSSLSocketImpl } // Temporarily use a different timeout for the handshake process - int savedTimeoutMilliseconds = getSoTimeout(); + int savedReadTimeoutMilliseconds = getSoTimeout(); + int savedWriteTimeoutMilliseconds = getSoWriteTimeout(); if (handshakeTimeoutMilliseconds >= 0) { setSoTimeout(handshakeTimeoutMilliseconds); + setSoWriteTimeout(handshakeTimeoutMilliseconds); } int sslSessionNativePointer; @@ -423,7 +406,8 @@ public class OpenSSLSocketImpl // Restore the original timeout now that the handshake is complete if (handshakeTimeoutMilliseconds >= 0) { - setSoTimeout(savedTimeoutMilliseconds); + setSoTimeout(savedReadTimeoutMilliseconds); + setSoWriteTimeout(savedWriteTimeoutMilliseconds); } // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback @@ -442,7 +426,7 @@ public class OpenSSLSocketImpl } } - private String getPeerHostName() { + String getPeerHostName() { if (wrappedHost != null) { return wrappedHost; } @@ -453,7 +437,7 @@ public class OpenSSLSocketImpl return null; } - private int getPeerPort() { + int getPeerPort() { return wrappedHost == null ? super.getPort() : wrappedPort; } @@ -594,8 +578,13 @@ public class OpenSSLSocketImpl } boolean client = sslParameters.getUseClientMode(); if (client) { - sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain, - authMethod); + X509TrustManager x509tm = sslParameters.getTrustManager(); + if (x509tm instanceof TrustManagerImpl) { + TrustManagerImpl tm = (TrustManagerImpl) x509tm; + tm.checkServerTrusted(peerCertificateChain, authMethod, wrappedHost); + } else { + x509tm.checkServerTrusted(peerCertificateChain, authMethod); + } } else { String authType = peerCertificateChain[0].getPublicKey().getAlgorithm(); sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain, @@ -715,7 +704,7 @@ public class OpenSSLSocketImpl return; } NativeCrypto.SSL_write(sslNativePointer, socket.getFileDescriptor$(), - OpenSSLSocketImpl.this, buf, offset, byteCount); + OpenSSLSocketImpl.this, buf, offset, byteCount, writeTimeoutMilliseconds); } } } @@ -793,35 +782,6 @@ public class OpenSSLSocketImpl } /** - * The names of the compression methods that may be used on this SSL - * connection. - * @return an array of compression methods - */ - public String[] getSupportedCompressionMethods() { - return NativeCrypto.getSupportedCompressionMethods(); - } - - /** - * The names of the compression methods versions that are in use - * on this SSL connection. - * - * @return an array of compression methods - */ - public String[] getEnabledCompressionMethods() { - return enabledCompressionMethods.clone(); - } - - /** - * Enables compression methods listed by getSupportedCompressionMethods(). - * - * @throws IllegalArgumentException when one or more of the names in the - * array are not supported, or when the array is null. - */ - public void setEnabledCompressionMethods(String[] methods) { - enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods); - } - - /** * This method enables session ticket support. * * @param useSessionTickets True to enable session tickets @@ -875,21 +835,42 @@ public class OpenSSLSocketImpl throw new SocketException("Methods sendUrgentData, setOOBInline are not supported."); } - @Override public void setSoTimeout(int timeoutMilliseconds) throws SocketException { - super.setSoTimeout(timeoutMilliseconds); - this.timeoutMilliseconds = timeoutMilliseconds; + @Override public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException { + super.setSoTimeout(readTimeoutMilliseconds); + this.readTimeoutMilliseconds = readTimeoutMilliseconds; } @Override public int getSoTimeout() throws SocketException { - return timeoutMilliseconds; + return readTimeoutMilliseconds; + } + + /** + * Note write timeouts are not part of the javax.net.ssl.SSLSocket API + */ + public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException { + this.writeTimeoutMilliseconds = writeTimeoutMilliseconds; + + StructTimeval tv = StructTimeval.fromMillis(writeTimeoutMilliseconds); + try { + Libcore.os.setsockoptTimeval(getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv); + } catch (ErrnoException errnoException) { + throw errnoException.rethrowAsSocketException(); + } + } + + /** + * Note write timeouts are not part of the javax.net.ssl.SSLSocket API + */ + public int getSoWriteTimeout() throws SocketException { + return writeTimeoutMilliseconds; } /** * Set the handshake timeout on this socket. This timeout is specified in * milliseconds and will be used only during the handshake process. */ - public void setHandshakeTimeout(int timeoutMilliseconds) throws SocketException { - this.handshakeTimeoutMilliseconds = timeoutMilliseconds; + public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException { + this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds; } @Override public void close() throws IOException { @@ -914,12 +895,13 @@ public class OpenSSLSocketImpl } } - 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/java/util/concurrent/locks/UnsafeAccess.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinEntryException.java index 07f64e4..8b74514 100644 --- a/luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinEntryException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -14,21 +14,16 @@ * limitations under the License. */ -package java.util.concurrent.locks; +package org.apache.harmony.xnet.provider.jsse; -import sun.misc.Unsafe; +// public for testing by CertPinManagerTest +public class PinEntryException extends Exception { -/** - * 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(); + PinEntryException() { + } - /** - * This class is uninstantiable. - */ - private UnsafeAccess() { - // This space intentionally left blank. + PinEntryException(String msg) { + super(msg); } } + diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java new file mode 100644 index 0000000..40b1838 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java @@ -0,0 +1,70 @@ +/* + * 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.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.List; +import libcore.io.Base64; +import libcore.io.DropBox; + +public class PinFailureLogger { + + private static final long LOG_INTERVAL_NANOS = 1000 * 1000 * 1000 * 60 * 60; + + private static long lastLoggedNanos = 0; + + public static synchronized void log(String cn, boolean chainContainsUserCert, + boolean pinIsEnforcing, + List<X509Certificate> chain) { + // if we've logged recently, don't do it again + if (!timeToLog()) { + return; + } + // otherwise, log the event + writeToLog(cn, chainContainsUserCert, pinIsEnforcing, chain); + // update the last logged time + lastLoggedNanos = System.nanoTime(); + } + + protected static synchronized void writeToLog(String cn, boolean chainContainsUserCert, + boolean pinIsEnforcing, + List<X509Certificate> chain) { + StringBuilder sb = new StringBuilder(); + sb.append(cn); + sb.append("|"); + sb.append(chainContainsUserCert); + sb.append("|"); + sb.append(pinIsEnforcing); + sb.append("|"); + for (X509Certificate cert : chain) { + try { + sb.append(Base64.encode(cert.getEncoded())); + } catch (CertificateEncodingException e) { + sb.append("Error: could not encode certificate"); + } + sb.append("|"); + } + DropBox.addText("cert_pin_failure", sb.toString()); + } + + protected static boolean timeToLog() { + long currentTimeNanos = System.nanoTime(); + return ((currentTimeNanos - lastLoggedNanos) > LOG_INTERVAL_NANOS); + } +} + diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java new file mode 100644 index 0000000..c05a391 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java @@ -0,0 +1,155 @@ +/* + * 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.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import libcore.io.EventLogger; + +/** + * This class represents a single entry in the pin file. + */ +// public for testing by CertPinManagerTest +public class PinListEntry { + + /** The Common Name (CN) as used on the SSL certificate */ + private final String cn; + + /** + * Determines whether a failed match here will prevent the chain from being accepted. If true, + * an unpinned chain will log and cause a match failure. If false, it will merely log. + */ + private final boolean enforcing; + + private final Set<String> pinnedFingerprints = new HashSet<String>(); + + private static final boolean DEBUG = false; + + private final TrustedCertificateStore certStore; + + public String getCommonName() { + return cn; + } + + public boolean getEnforcing() { + return enforcing; + } + + public PinListEntry(String entry, TrustedCertificateStore store) throws PinEntryException { + if (entry == null) { + throw new NullPointerException("entry == null"); + } + certStore = store; + // Examples: + // *.google.com=true|34c8a0d...9e04ca05f,9e04ca05f...34c8a0d + // *.android.com=true|ca05f...8a0d34c + // clients.google.com=false|9e04ca05f...34c8a0d,34c8a0d...9e04ca05f + String[] values = entry.split("[=,|]"); + // entry must have a CN, an enforcement value, and at least one pin + if (values.length < 3) { + throw new PinEntryException("Received malformed pin entry"); + } + // get the cn + cn = values[0]; // is there more validation we can do here? + enforcing = enforcementValueFromString(values[1]); + // the remainder should be pins + addPins(Arrays.copyOfRange(values, 2, values.length)); + } + + private static boolean enforcementValueFromString(String val) throws PinEntryException { + if (val.equals("true")) { + return true; + } else if (val.equals("false")) { + return false; + } else { + throw new PinEntryException("Enforcement status is not a valid value"); + } + } + + /** + * Checks the given chain against the pin list corresponding to this entry. + * + * If the pin list does not contain the required certs and the enforcing field is true then + * this returns true, indicating a verification error. Otherwise, it returns false and + * verification should proceed. + */ + public boolean chainIsNotPinned(List<X509Certificate> chain) { + for (X509Certificate cert : chain) { + String fingerprint = getFingerprint(cert); + if (pinnedFingerprints.contains(fingerprint)) { + return false; + } + } + logPinFailure(chain); + return enforcing; + } + + private static String getFingerprint(X509Certificate cert) { + try { + MessageDigest dgst = MessageDigest.getInstance("SHA512"); + byte[] encoded = cert.getPublicKey().getEncoded(); + byte[] fingerprint = dgst.digest(encoded); + return IntegralToString.bytesToHexString(fingerprint, false); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private void addPins(String[] pins) { + for (String pin : pins) { + validatePin(pin); + } + Collections.addAll(pinnedFingerprints, pins); + } + + private static void validatePin(String pin) { + // check to make sure the length is correct + if (pin.length() != 128) { + throw new IllegalArgumentException("Pin is not a valid length"); + } + // check to make sure that it's a valid hex string + try { + new BigInteger(pin, 16); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Pin is not a valid hex string", e); + } + } + + private boolean chainContainsUserCert(List<X509Certificate> chain) { + if (certStore == null) { + return false; + } + for (X509Certificate cert : chain) { + if (certStore.isUserAddedCertificate(cert)) { + return true; + } + } + return false; + } + + private void logPinFailure(List<X509Certificate> chain) { + PinFailureLogger.log(cn, chainContainsUserCert(chain), enforcing, chain); + } +} + diff --git a/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinManagerException.java index 96fff17..74b3c65 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinManagerException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -14,21 +14,19 @@ * limitations under the License. */ -package java.util.concurrent.atomic; +package org.apache.harmony.xnet.provider.jsse; -import sun.misc.Unsafe; +class PinManagerException extends Exception { -/** - * 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(); + PinManagerException() { + } - /** - * This class is uninstantiable. - */ - private UnsafeAccess() { - // This space intentionally left blank. + PinManagerException(String msg) { + super(msg); + } + + PinManagerException(String msg, Exception e) { + super(msg, e); } } + diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java index be9a7fc..93496cf 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java @@ -88,7 +88,7 @@ public class SSLSocketFactoryImpl extends SSLSocketFactory { if (instantiationException != null) { throw instantiationException; } - return new SSLSocketWrapper(s, autoClose, (SSLParametersImpl) sslParameters + return new SSLSocketWrapper(s, host, port, autoClose, (SSLParametersImpl) sslParameters .clone()); } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java index 6e5fddd..2cd2cf5 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java @@ -41,6 +41,10 @@ public class SSLSocketImpl extends SSLSocket { // indicates if handshake has been started private boolean handshake_started = false; + // used when we're wrapping a socket + private final String wrappedHost; + private final int wrappedPort; + // record protocol to be used protected SSLRecordProtocol recordProtocol; // handshake protocol to be used @@ -83,6 +87,8 @@ public class SSLSocketImpl extends SSLSocket { */ protected SSLSocketImpl(SSLParametersImpl sslParameters) { this.sslParameters = sslParameters; + this.wrappedHost = null; + this.wrappedPort = -1; // init should be called after creation! } @@ -99,6 +105,8 @@ public class SSLSocketImpl extends SSLSocket { protected SSLSocketImpl(String host, int port, SSLParametersImpl sslParameters) throws IOException, UnknownHostException { super(host, port); + this.wrappedHost = host; + this.wrappedPort = port; this.sslParameters = sslParameters; init(); } @@ -120,6 +128,8 @@ public class SSLSocketImpl extends SSLSocket { SSLParametersImpl sslParameters) throws IOException, UnknownHostException { super(host, port, localHost, localPort); + this.wrappedHost = host; + this.wrappedPort = port; this.sslParameters = sslParameters; init(); } @@ -138,6 +148,8 @@ public class SSLSocketImpl extends SSLSocket { SSLParametersImpl sslParameters) throws IOException { super(host, port); this.sslParameters = sslParameters; + this.wrappedHost = null; + this.wrappedPort = -1; init(); } @@ -158,6 +170,8 @@ public class SSLSocketImpl extends SSLSocket { SSLParametersImpl sslParameters) throws IOException { super(address, port, localAddress, localPort); this.sslParameters = sslParameters; + this.wrappedHost = null; + this.wrappedPort = -1; init(); } @@ -193,6 +207,29 @@ public class SSLSocketImpl extends SSLSocket { } } + String getWrappedHostName() { + return wrappedHost; + } + + int getWrappedPort() { + return wrappedPort; + } + + String getPeerHostName() { + if (wrappedHost != null) { + return wrappedHost; + } + InetAddress inetAddress = super.getInetAddress(); + if (inetAddress != null) { + return inetAddress.getHostName(); + } + return null; + } + + int getPeerPort() { + return (wrappedPort == -1) ? super.getPort() : wrappedPort; + } + // --------------- SSLParameters based methods --------------------- /** diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java index 27bbead..a393e24 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java @@ -32,8 +32,9 @@ public class SSLSocketWrapper extends SSLSocketImpl { private final Socket socket; private final boolean autoClose; - protected SSLSocketWrapper(Socket socket, boolean autoClose, SSLParametersImpl sslParameters) throws IOException { - super(sslParameters); + protected SSLSocketWrapper(Socket socket, String host, int port, boolean autoClose, + SSLParametersImpl sslParameters) throws IOException { + super(host, port, sslParameters); if (!socket.isConnected()) { throw new SocketException("Socket is not connected."); } 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 c5e1838..fa8d291 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 @@ package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; import java.math.BigInteger; -import java.security.AccessController; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -659,7 +658,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { } 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/TrustManagerImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java index 3f362c5..0218249 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java @@ -35,6 +35,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import javax.net.ssl.X509TrustManager; +import libcore.io.EventLogger; /** * @@ -52,6 +53,11 @@ public final class TrustManagerImpl implements X509TrustManager { private final KeyStore rootKeyStore; /** + * The CertPinManager, which validates the chain against a host-to-pin mapping + */ + private CertPinManager pinManager; + + /** * The backing store for the AndroidCAStore if non-null. This will * be null when the rootKeyStore is null, implying we are not * using the AndroidCAStore. @@ -83,6 +89,13 @@ public final class TrustManagerImpl implements X509TrustManager { * @param ks */ public TrustManagerImpl(KeyStore keyStore) { + this(keyStore, null); + } + + /** + * For testing only + */ + public TrustManagerImpl(KeyStore keyStore, CertPinManager manager) { CertPathValidator validatorLocal = null; CertificateFactory factoryLocal = null; KeyStore rootKeyStoreLocal = null; @@ -111,6 +124,17 @@ public final class TrustManagerImpl implements X509TrustManager { } catch (Exception e) { errLocal = e; } + + if (manager != null) { + this.pinManager = manager; + } else { + try { + pinManager = new CertPinManager(trustedCertificateStoreLocal); + } catch (PinManagerException e) { + throw new SecurityException("Could not initialize CertPinManager", e); + } + } + this.rootKeyStore = rootKeyStoreLocal; this.trustedCertificateStore = trustedCertificateStoreLocal; this.validator = validatorLocal; @@ -155,12 +179,22 @@ public final class TrustManagerImpl implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - checkTrusted(chain, authType); + checkTrusted(chain, authType, null); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - checkTrusted(chain, authType); + checkTrusted(chain, authType, null); + } + + /** + * Validates whether a server is trusted. If hostname is given and non-null it also checks if + * chain is pinned appropriately for that host. If null, it does not check for pinned certs. + * The return value is a list of the certificates used for making the trust decision. + */ + public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, + String host) throws CertificateException { + return checkTrusted(chain, authType, host); } public void handleTrustStorageUpdate() { @@ -168,10 +202,10 @@ public final class TrustManagerImpl implements X509TrustManager { trustedCertificateIndex.reset(); } else { trustedCertificateIndex.reset(trustAnchors(acceptedIssuers)); - } + } } - private void checkTrusted(X509Certificate[] chain, String authType) + private List<X509Certificate> checkTrusted(X509Certificate[] chain, String authType, String host) throws CertificateException { if (chain == null || chain.length == 0 || authType == null || authType.length() == 0) { throw new IllegalArgumentException("null or zero-length parameter"); @@ -180,21 +214,71 @@ public final class TrustManagerImpl implements X509TrustManager { throw new CertificateException(err); } - Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>(); - X509Certificate[] newChain = cleanupCertChainAndFindTrustAnchors(chain, trustAnchors); + // get the cleaned up chain and trust anchor + Set<TrustAnchor> trustAnchor = new HashSet<TrustAnchor>(); // there can only be one! + X509Certificate[] newChain = cleanupCertChainAndFindTrustAnchors(chain, trustAnchor); + + // add the first trust anchor to the chain, which may be an intermediate + List<X509Certificate> wholeChain = new ArrayList<X509Certificate>(); + wholeChain.addAll(Arrays.asList(newChain)); + // trustAnchor is actually just a single element + for (TrustAnchor trust : trustAnchor) { + wholeChain.add(trust.getTrustedCert()); + } + + // add all the cached certificates from the cert index, avoiding loops + // this gives us a full chain from leaf to root, which we use for cert pinning and pass + // back out to callers when we return. + X509Certificate last = wholeChain.get(wholeChain.size() - 1); + while (true) { + TrustAnchor cachedTrust = trustedCertificateIndex.findByIssuerAndSignature(last); + // the cachedTrust can be null if there isn't anything in the index or if a user has + // trusted a non-self-signed cert. + if (cachedTrust == null) { + break; + } + + // at this point we have a cached trust anchor, but don't know if its one we got from + // the server. Extract the cert, compare it to the last element in the chain, and add it + // if we haven't seen it before. + X509Certificate next = cachedTrust.getTrustedCert(); + if (next != last) { + wholeChain.add(next); + last = next; + } else { + // if next == last then we found a self-signed cert and the chain is done + break; + } + } + + // build the cert path from the array of certs sans trust anchors + CertPath certPath = factory.generateCertPath(Arrays.asList(newChain)); + + if (host != null) { + boolean chainIsNotPinned = true; + try { + chainIsNotPinned = pinManager.chainIsNotPinned(host, wholeChain); + } catch (PinManagerException e) { + throw new CertificateException(e); + } + if (chainIsNotPinned) { + throw new CertificateException(new CertPathValidatorException( + "Certificate path is not properly pinned.", null, certPath, -1)); + } + } + if (newChain.length == 0) { // chain was entirely trusted, skip the validator - return; + return wholeChain; } - CertPath certPath = factory.generateCertPath(Arrays.asList(newChain)); - if (trustAnchors.isEmpty()) { + if (trustAnchor.isEmpty()) { throw new CertificateException(new CertPathValidatorException( "Trust anchor for certification path not found.", null, certPath, -1)); } try { - PKIXParameters params = new PKIXParameters(trustAnchors); + PKIXParameters params = new PKIXParameters(trustAnchor); params.setRevocationEnabled(false); validator.validate(certPath, params); // Add intermediate CAs to the index to tolerate sites @@ -211,6 +295,8 @@ public final class TrustManagerImpl implements X509TrustManager { } catch (CertPathValidatorException e) { throw new CertificateException(e); } + + return wholeChain; } /** @@ -232,17 +318,9 @@ public final class TrustManagerImpl implements X509TrustManager { // Start with the first certificate in the chain, assuming it // is the leaf certificate (server or client cert). for (currIndex = 0; currIndex < chain.length; currIndex++) { - // If the current cert is a TrustAnchor, we can ignore the rest of the chain. - // This avoids including "bridge" CA certs that added for legacy compatability. - TrustAnchor trustAnchor = findTrustAnchorBySubjectAndPublicKey(chain[currIndex]); - if (trustAnchor != null) { - trustAnchors.add(trustAnchor); - currIndex--; - break; - } - // Walk the rest of the chain to find a "subject" matching + // Walk the chain to find a "subject" matching // the "issuer" of the current certificate. In a properly - // order chain this should be the next cert and be fast. + // ordered chain this should be the next cert and be fast. // If not, we reorder things to be as the validator will // expect. boolean foundNext = false; @@ -271,15 +349,27 @@ public final class TrustManagerImpl implements X509TrustManager { } } - // 2. If the chain is now shorter, copy to an appropriately sized array. - int chainLength = currIndex + 1; + // 2. Find the trust anchor in the chain, if any + int anchorIndex; + for (anchorIndex = 0; anchorIndex < chain.length; anchorIndex++) { + // If the current cert is a TrustAnchor, we can ignore the rest of the chain. + // This avoids including "bridge" CA certs that added for legacy compatibility. + TrustAnchor trustAnchor = findTrustAnchorBySubjectAndPublicKey(chain[anchorIndex]); + if (trustAnchor != null) { + trustAnchors.add(trustAnchor); + break; + } + } + + // 3. If the chain is now shorter, copy to an appropriately sized array. + int chainLength = anchorIndex; X509Certificate[] newChain = ((chainLength == chain.length) ? chain : Arrays.copyOf(chain, chainLength)); - // 3. If no TrustAnchor was found in cleanup, look for one now + // 4. If we didn't find a trust anchor earlier, look for one now if (trustAnchors.isEmpty()) { - TrustAnchor trustAnchor = findTrustAnchorByIssuerAndSignature(newChain[chainLength-1]); + TrustAnchor trustAnchor = findTrustAnchorByIssuerAndSignature(newChain[anchorIndex-1]); if (trustAnchor != null) { trustAnchors.add(trustAnchor); } 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..e7b1a7c 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.FileOutputStream; 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 @@ -292,6 +298,14 @@ public final class TrustedCertificateStore { } /** + * Returns true to indicate that the certificate was added by the + * user, false otherwise. + */ + public boolean isUserAddedCertificate(X509Certificate cert) { + return getCertificateFile(addedDir, cert).exists(); + } + + /** * Returns a File for where the certificate is found if it exists * or where it should be installed if it does not exist. The * caller can disambiguate these cases by calling {@code @@ -366,6 +380,109 @@ public final class TrustedCertificateStore { 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 jniThrowExceptionWithErrno(JNIEnv* env, const char* exceptionClassName, int 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 @@ static void Bidi_ubidi_setPara(JNIEnv* env, jclass, jlong ptr, jcharArray text, } 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 jbyte Bidi_ubidi_getParaLevel(JNIEnv*, jclass, jlong ptr) { 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 jbyteArray Bidi_ubidi_getLevels(JNIEnv* env, jclass, jlong ptr) { 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 @@ static jobjectArray Bidi_ubidi_getRuns(JNIEnv* env, jclass, jlong ptr) { 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 @@ public: if (mJavaInput) { mEnv->ReleaseStringChars(mJavaInput, mChars); } - maybeThrowIcuException(mEnv, mStatus); + maybeThrowIcuException(mEnv, "utext_close", mStatus); } RegexMatcher* operator->() { @@ -173,7 +173,7 @@ static jint Matcher_openImpl(JNIEnv* env, jclass, jint patternAddr) { 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 (mBundle != NULL) { - ures_close(mBundle); - } + ~ScopedResourceBundle() { + if (bundle_ != NULL) { + ures_close(bundle_); } + } - UResourceBundle* get() { - return mBundle; - } + UResourceBundle* get() { + return bundle_; + } -private: - UResourceBundle* mBundle; + bool hasKey(const char* key) { + UErrorCode status = U_ZERO_ERROR; + ures_getStringByKey(bundle_, key, NULL, &status); + return U_SUCCESS(status); + } - // 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 setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, } 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 @@ static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, jstring locale 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. - } - 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. - } + return JNI_FALSE; // ICU has a fixed-length limit. + } - 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 @@ static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobj 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 @@ static jboolean ICU_initLocaleDataImpl(JNIEnv* env, jclass, jstring locale, jobj 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 @@ static jobject ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) { 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 @@ public: 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 @@ static jint makeIterator(JNIEnv* env, jstring locale, UBreakIteratorType type) { 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_compare(JNIEnv* env, jclass, jint address, jstring j 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 @@ static jint NativeCollation_getCollationElementIterator(JNIEnv* env, jclass, jin } 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 jbyteArray NativeCollation_getSortKey(JNIEnv* env, jclass, jint address, 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 @@ static jint NativeCollation_openCollator(JNIEnv* env, jclass, jstring localeName } 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 @@ static jint NativeCollation_openCollatorFromRules(JNIEnv* env, jclass, jstring j 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 @@ static jint NativeCollation_safeClone(JNIEnv* env, jclass, jint address) { 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 @@ static void NativeCollation_setText(JNIEnv* env, jclass, jint address, jstring j } 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 @@ static jlong NativeConverter_openConverter(JNIEnv* env, jclass, jstring converte } 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 @@ static void NativeConverter_closeConverter(JNIEnv*, jclass, jlong address) { 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 @@ static jint NativeConverter_encode(JNIEnv* env, jclass, jlong address, 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 @@ static jint NativeConverter_decode(JNIEnv* env, jclass, jlong address, 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 @@ static jint NativeConverter_decode(JNIEnv* env, jclass, jlong address, } } + // Managed code handles some cases; throw all other errors. + if (shouldCodecThrow(flush, errorCode)) { + maybeThrowIcuException(env, "ucnv_toUnicode", errorCode); + } return errorCode; } @@ -387,11 +412,12 @@ static UConverterFromUCallback getFromUCallback(int32_t mode) { 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 @@ static jint NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address 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 void CHARSET_DECODER_CALLBACK(const void* rawContext, UConverterToUnicode } } -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 @@ static jint NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address 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 @@ static JNINativeMethod gMethods[] = { 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 @@ static jint NativeDecimalFormat_open(JNIEnv* env, jclass, jstring pattern0, 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 @@ static void NativeDecimalFormat_setSymbol(JNIEnv* env, jclass, jint addr, jint j 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 @@ static void NativeDecimalFormat_setTextAttribute(JNIEnv* env, jclass, jint addr, 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 @@ static jstring NativeDecimalFormat_getTextAttribute(JNIEnv* env, jclass, jint ad 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 @@ static void NativeDecimalFormat_applyPatternImpl(JNIEnv* env, jclass, jint addr, 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 @@ static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jint addr, jstring 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 @@ static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jint addr, jstring 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 @@ static jstring NativeNormalizer_normalizeImpl(JNIEnv* env, jclass, jstring s, ji 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 @@ static jboolean NativeNormalizer_isNormalizedImpl(JNIEnv* env, jclass, jstring s 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 @@ static jint NativePluralRules_forLocaleImpl(JNIEnv* env, jclass, jstring localeN 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 @@ static jobjectArray TimeZones_forCountryCode(JNIEnv* env, jclass, jstring countr } 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 void Memory_pokeInt(JNIEnv*, jclass, jint dstAddress, jint value, jboolea 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 jlong Memory_peekLong(JNIEnv*, jclass, jint srcAddress, jboolean swap) { } 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 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { 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 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) { 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..acb3dc2 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 @@ struct addrinfo_deleter { }; /** - * 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 @@ static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaIn 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 @@ static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress 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 @@ static void Posix_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) { 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 @@ static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddr 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 @@ static jobjectArray Posix_environ(JNIEnv* env, jobject) { 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 jint Posix_ioctlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobje 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 @@ static jint Posix_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject ja 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 @@ static jint Posix_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject java 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 @@ static void Posix_setgid(JNIEnv* env, jobject, jint gid) { 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 @@ static void Posix_setsockoptGroupReq(JNIEnv* env, jobject, jobject javaFd, jint 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; @@ -1149,6 +1168,15 @@ static jobject Posix_socket(JNIEnv* env, jobject, jint domain, jint type, jint p return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL; } +static void Posix_socketpair(JNIEnv* env, jobject, jint domain, jint type, jint protocol, jobject javaFd1, jobject javaFd2) { + int fds[2]; + int rc = throwIfMinusOne(env, "socketpair", TEMP_FAILURE_RETRY(socketpair(domain, type, protocol, fds))); + if (rc != -1) { + jniSetFileDescriptorOfFD(env, javaFd1, fds[0]); + jniSetFileDescriptorOfFD(env, javaFd2, fds[1]); + } +} + static jobject Posix_stat(JNIEnv* env, jobject, jstring javaPath) { return doStat(env, javaPath, false); } @@ -1195,6 +1223,15 @@ static jlong Posix_sysconf(JNIEnv* env, jobject, jint name) { 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 +1273,14 @@ static JNINativeMethod gMethods[] = { 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 +1313,7 @@ static JNINativeMethod gMethods[] = { 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 +1339,7 @@ static JNINativeMethod gMethods[] = { 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"), @@ -1308,11 +1350,14 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Posix, setuid, "(I)V"), NATIVE_METHOD(Posix, shutdown, "(Ljava/io/FileDescriptor;I)V"), NATIVE_METHOD(Posix, socket, "(III)Ljava/io/FileDescriptor;"), + NATIVE_METHOD(Posix, socketpair, "(IIILjava/io/FileDescriptor;Ljava/io/FileDescriptor;)V"), NATIVE_METHOD(Posix, stat, "(Ljava/lang/String;)Llibcore/io/StructStat;"), NATIVE_METHOD(Posix, statfs, "(Ljava/lang/String;)Llibcore/io/StructStatFs;"), 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 @@ static void RawSocket_create(JNIEnv* env, jclass, jobject fileDescriptor, return; } + sockunion su; memset(&su, 0, sizeof(su)); su.sll.sll_family = PF_PACKET; su.sll.sll_protocol = htons(protocolType); @@ -119,6 +120,7 @@ static int RawSocket_sendPacket(JNIEnv* env, jclass, jobject fileDescriptor, 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 b11e30a..322f416 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 @@ struct X509_Delete { }; 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 @@ static void freeOpenSslErrorState(void) { 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 @@ static void freeOpenSslErrorState(void) { * @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; } @@ -442,11 +505,11 @@ static bool arrayToBignum(JNIEnv* env, jbyteArray source, BIGNUM** dest) { /** * Converts an OpenSSL BIGNUM to a Java byte[] array. */ -static jbyteArray bignumToArray(JNIEnv* env, BIGNUM* source) { - JNI_TRACE("bignumToArray(%p)", source); +static jbyteArray bignumToArray(JNIEnv* env, BIGNUM* source, const char* sourceName) { + JNI_TRACE("bignumToArray(%p, %s)", source, sourceName); if (source == NULL) { - jniThrowNullPointerException(env, NULL); + jniThrowNullPointerException(env, sourceName); return NULL; } @@ -454,7 +517,7 @@ static jbyteArray bignumToArray(JNIEnv* env, BIGNUM* source) { jbyteArray javaBytes = env->NewByteArray(len); ScopedByteArrayRW bytes(env, javaBytes); if (bytes.get() == NULL) { - JNI_TRACE("bignumToArray(%p) => NULL", source); + JNI_TRACE("bignumToArray(%p, %s) => NULL", source, sourceName); return NULL; } @@ -472,7 +535,7 @@ static jbyteArray bignumToArray(JNIEnv* env, BIGNUM* source) { return NULL; } - JNI_TRACE("bignumToArray(%p) => %p", source, javaBytes); + JNI_TRACE("bignumToArray(%p, %s) => %p", source, sourceName, javaBytes); return javaBytes; } @@ -577,6 +640,27 @@ static jint NativeCrypto_ENGINE_by_id(JNIEnv* env, jclass, jstring idJava) { 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); @@ -839,6 +923,26 @@ static void NativeCrypto_EVP_PKEY_free(JNIEnv*, jclass, EVP_PKEY* pkey) { } } +static jint NativeCrypto_EVP_PKEY_cmp(JNIEnv* env, jclass, jint pkey1Ref, jint pkey2Ref) { + EVP_PKEY* pkey1 = reinterpret_cast<EVP_PKEY*>(pkey1Ref); + EVP_PKEY* pkey2 = reinterpret_cast<EVP_PKEY*>(pkey2Ref); + JNI_TRACE("EVP_PKEY_cmp(%p, %p)", pkey1, pkey2); + + if (pkey1 == NULL) { + JNI_TRACE("EVP_PKEY_cmp(%p, %p) => failed pkey1 == NULL", pkey1, pkey2); + jniThrowNullPointerException(env, "pkey1 == NULL"); + return -1; + } else if (pkey2 == NULL) { + JNI_TRACE("EVP_PKEY_cmp(%p, %p) => failed pkey2 == NULL", pkey1, pkey2); + jniThrowNullPointerException(env, "pkey2 == NULL"); + return -1; + } + + int result = EVP_PKEY_cmp(pkey1, pkey2); + JNI_TRACE("EVP_PKEY_cmp(%p, %p) => %d", pkey1, pkey2, result); + return result; +} + /* * static native byte[] i2d_PKCS8_PRIV_KEY_INFO(int, byte[]) */ @@ -1010,6 +1114,78 @@ static jint NativeCrypto_RSA_generate_key_ex(JNIEnv* env, jclass, jint modulusBi 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); */ @@ -1028,13 +1204,13 @@ static jobjectArray NativeCrypto_get_RSA_public_params(JNIEnv* env, jclass, jint return NULL; } - jbyteArray n = bignumToArray(env, rsa->n); + jbyteArray n = bignumToArray(env, rsa->n, "n"); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(joa, 0, n); - jbyteArray e = bignumToArray(env, rsa->e); + jbyteArray e = bignumToArray(env, rsa->e, "e"); if (env->ExceptionCheck()) { return NULL; } @@ -1061,14 +1237,14 @@ static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jin return NULL; } - jbyteArray n = bignumToArray(env, rsa->n); + jbyteArray n = bignumToArray(env, rsa->n, "n"); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(joa, 0, n); if (rsa->e != NULL) { - jbyteArray e = bignumToArray(env, rsa->e); + jbyteArray e = bignumToArray(env, rsa->e, "e"); if (env->ExceptionCheck()) { return NULL; } @@ -1076,7 +1252,7 @@ static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jin } if (rsa->d != NULL) { - jbyteArray d = bignumToArray(env, rsa->d); + jbyteArray d = bignumToArray(env, rsa->d, "d"); if (env->ExceptionCheck()) { return NULL; } @@ -1084,7 +1260,7 @@ static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jin } if (rsa->p != NULL) { - jbyteArray p = bignumToArray(env, rsa->p); + jbyteArray p = bignumToArray(env, rsa->p, "p"); if (env->ExceptionCheck()) { return NULL; } @@ -1092,7 +1268,7 @@ static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jin } if (rsa->q != NULL) { - jbyteArray q = bignumToArray(env, rsa->q); + jbyteArray q = bignumToArray(env, rsa->q, "q"); if (env->ExceptionCheck()) { return NULL; } @@ -1100,7 +1276,7 @@ static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jin } if (rsa->dmp1 != NULL) { - jbyteArray dmp1 = bignumToArray(env, rsa->dmp1); + jbyteArray dmp1 = bignumToArray(env, rsa->dmp1, "dmp1"); if (env->ExceptionCheck()) { return NULL; } @@ -1108,7 +1284,7 @@ static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jin } if (rsa->dmq1 != NULL) { - jbyteArray dmq1 = bignumToArray(env, rsa->dmq1); + jbyteArray dmq1 = bignumToArray(env, rsa->dmq1, "dmq1"); if (env->ExceptionCheck()) { return NULL; } @@ -1116,7 +1292,7 @@ static jobjectArray NativeCrypto_get_RSA_private_params(JNIEnv* env, jclass, jin } if (rsa->iqmp != NULL) { - jbyteArray iqmp = bignumToArray(env, rsa->iqmp); + jbyteArray iqmp = bignumToArray(env, rsa->iqmp, "iqmp"); if (env->ExceptionCheck()) { return NULL; } @@ -1221,26 +1397,26 @@ static jobjectArray NativeCrypto_get_DSA_params(JNIEnv* env, jclass, jint pkeyRe return NULL; } - jbyteArray g = bignumToArray(env, dsa->g); + jbyteArray g = bignumToArray(env, dsa->g, "g"); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(joa, 0, g); - jbyteArray p = bignumToArray(env, dsa->p); + jbyteArray p = bignumToArray(env, dsa->p, "p"); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(joa, 1, p); - jbyteArray q = bignumToArray(env, dsa->q); + jbyteArray q = bignumToArray(env, dsa->q, "q"); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(joa, 2, q); if (dsa->pub_key != NULL) { - jbyteArray pub_key = bignumToArray(env, dsa->pub_key); + jbyteArray pub_key = bignumToArray(env, dsa->pub_key, "pub_key"); if (env->ExceptionCheck()) { return NULL; } @@ -1248,7 +1424,7 @@ static jobjectArray NativeCrypto_get_DSA_params(JNIEnv* env, jclass, jint pkeyRe } if (dsa->priv_key != NULL) { - jbyteArray priv_key = bignumToArray(env, dsa->priv_key); + jbyteArray priv_key = bignumToArray(env, dsa->priv_key, "priv_key"); if (env->ExceptionCheck()) { return NULL; } @@ -1634,6 +1810,13 @@ static jint NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass, jint ctxRef, jbyte if (ok < 0) { throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyFinal"); } + + /* + * For DSA keys, OpenSSL appears to have a bug where it returns + * errors for any result != 1. See dsa_ossl.c in dsa_do_verify + */ + freeOpenSslErrorState(); + JNI_TRACE("NativeCrypto_EVP_VerifyFinal(%p, %p, %d, %d, %p) => %d", ctx, buffer, offset, length, pkey, ok); @@ -1828,6 +2011,24 @@ static jint NativeCrypto_RAND_load_file(JNIEnv* env, jclass, jstring filename, j 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 @@ -2070,12 +2271,15 @@ class AppData { static AppData* create() { UniquePtr<AppData> appData(new AppData()); if (pipe(appData.get()->fdsEmergency) == -1) { + ALOGE("AppData::create pipe(2) failed: %s", strerror(errno)); return NULL; } if (!setBlocking(appData.get()->fdsEmergency[0], false)) { + ALOGE("AppData::create fcntl(2) failed: %s", strerror(errno)); return NULL; } if (MUTEX_SETUP(appData.get()->mutex) == -1) { + ALOGE("pthread_mutex_init(3) failed: %s", strerror(errno)); return NULL; } return appData.release(); @@ -2364,11 +2568,19 @@ static void info_callback(const SSL* ssl, int where, int ret __attribute__ ((unu } /** - * 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 +2590,7 @@ static int client_cert_cb(SSL* ssl, X509** x509Out, EVP_PKEY** pkeyOut) { } 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 +2637,17 @@ static int client_cert_cb(SSL* ssl, X509** x509Out, EVP_PKEY** pkeyOut) { 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 +2987,7 @@ static void NativeCrypto_SSL_use_certificate(JNIEnv* env, jclass, 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 +3003,9 @@ static void NativeCrypto_SSL_use_certificate(JNIEnv* env, jclass, 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 +3014,9 @@ static void NativeCrypto_SSL_use_certificate(JNIEnv* env, jclass, } } - 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 +3032,7 @@ static void NativeCrypto_SSL_use_certificate(JNIEnv* env, jclass, 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 +3394,10 @@ static int next_proto_select_callback(SSL* ssl, unsigned char **out, unsigned ch 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) { @@ -3555,7 +3765,7 @@ static jobjectArray NativeCrypto_SSL_get_peer_cert_chain(JNIEnv* env, jclass, ji * cleanly shut down, or THROW_SSLEXCEPTION if an exception should be thrown. */ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* buf, jint len, - int* sslReturnCode, int* sslErrorCode, int timeout_millis) { + int* sslReturnCode, int* sslErrorCode, int read_timeout_millis) { JNI_TRACE("ssl=%p sslRead buf=%p len=%d", ssl, buf, len); if (len == 0) { @@ -3634,7 +3844,7 @@ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* b // Need to wait for availability of underlying layer, then retry. case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { - int selectResult = sslSelect(env, sslError, fdObject, appData, timeout_millis); + int selectResult = sslSelect(env, sslError, fdObject, appData, read_timeout_millis); if (selectResult == THROWN_EXCEPTION) { return THROWN_EXCEPTION; } @@ -3686,11 +3896,11 @@ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* b */ static jint NativeCrypto_SSL_read(JNIEnv* env, jclass, jint ssl_address, jobject fdObject, jobject shc, jbyteArray b, jint offset, jint len, - jint timeout_millis) + jint read_timeout_millis) { SSL* ssl = to_SSL(env, ssl_address, true); - JNI_TRACE("ssl=%p NativeCrypto_SSL_read fd=%p shc=%p b=%p offset=%d len=%d timeout_millis=%d", - ssl, fdObject, shc, b, offset, len, timeout_millis); + JNI_TRACE("ssl=%p NativeCrypto_SSL_read fd=%p shc=%p b=%p offset=%d len=%d read_timeout_millis=%d", + ssl, fdObject, shc, b, offset, len, read_timeout_millis); if (ssl == NULL) { return 0; } @@ -3714,7 +3924,7 @@ static jint NativeCrypto_SSL_read(JNIEnv* env, jclass, jint ssl_address, jobject int sslErrorCode = SSL_ERROR_NONE;; int ret = sslRead(env, ssl, fdObject, shc, reinterpret_cast<char*>(bytes.get() + offset), len, - &returnCode, &sslErrorCode, timeout_millis); + &returnCode, &sslErrorCode, read_timeout_millis); int result; switch (ret) { @@ -3754,8 +3964,9 @@ static jint NativeCrypto_SSL_read(JNIEnv* env, jclass, jint ssl_address, jobject * cleanly shut down, or THROW_SSLEXCEPTION if an exception should be thrown. */ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const char* buf, jint len, - int* sslReturnCode, int* sslErrorCode) { - JNI_TRACE("ssl=%p sslWrite buf=%p len=%d", ssl, buf, len); + int* sslReturnCode, int* sslErrorCode, int write_timeout_millis) { + JNI_TRACE("ssl=%p sslWrite buf=%p len=%d write_timeout_millis=%d", + ssl, buf, len, write_timeout_millis); if (len == 0) { // Don't bother doing anything in this case. @@ -3840,7 +4051,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const // it's also not standard Java behavior, so we wait forever here. case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { - int selectResult = sslSelect(env, sslError, fdObject, appData, 0); + int selectResult = sslSelect(env, sslError, fdObject, appData, write_timeout_millis); if (selectResult == THROWN_EXCEPTION) { return THROWN_EXCEPTION; } @@ -3891,11 +4102,11 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const * OpenSSL write function (2): write into buffer at offset n chunks. */ static void NativeCrypto_SSL_write(JNIEnv* env, jclass, jint ssl_address, jobject fdObject, - jobject shc, jbyteArray b, jint offset, jint len) + jobject shc, jbyteArray b, jint offset, jint len, jint write_timeout_millis) { SSL* ssl = to_SSL(env, ssl_address, true); - JNI_TRACE("ssl=%p NativeCrypto_SSL_write fd=%p shc=%p b=%p offset=%d len=%d", - ssl, fdObject, shc, b, offset, len); + JNI_TRACE("ssl=%p NativeCrypto_SSL_write fd=%p shc=%p b=%p offset=%d len=%d write_timeout_millis=%d", + ssl, fdObject, shc, b, offset, len, write_timeout_millis); if (ssl == NULL) { return; } @@ -3918,7 +4129,7 @@ static void NativeCrypto_SSL_write(JNIEnv* env, jclass, jint ssl_address, jobjec int returnCode = 0; int sslErrorCode = SSL_ERROR_NONE; int ret = sslWrite(env, ssl, fdObject, shc, reinterpret_cast<const char*>(bytes.get() + offset), - len, &returnCode, &sslErrorCode); + len, &returnCode, &sslErrorCode, write_timeout_millis); switch (ret) { case THROW_SSLEXCEPTION: @@ -3937,7 +4148,7 @@ static void NativeCrypto_SSL_write(JNIEnv* env, jclass, jint ssl_address, jobjec } /** - * 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) { @@ -4126,43 +4337,6 @@ static jstring NativeCrypto_SSL_SESSION_cipher(JNIEnv* env, jclass, jint ssl_ses } /** - * Gets and returns in a string the compression method negotiated for the SSL session. - */ -static jstring NativeCrypto_SSL_SESSION_compress_meth(JNIEnv* env, jclass, - jint ssl_ctx_address, - jint ssl_session_address) { - SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); - SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true); - JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_compress_meth ssl_ctx=%p", - ssl_session, ssl_ctx); - if (ssl_ctx == NULL || ssl_session == NULL) { - return NULL; - } - - int compress_meth = ssl_session->compress_meth; - if (compress_meth == 0) { - const char* name = "NULL"; - JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_compress_meth => %s", ssl_session, name); - return env->NewStringUTF(name); - } - - int num_comp_methods = sk_SSL_COMP_num(ssl_ctx->comp_methods); - for (int i = 0; i < num_comp_methods; i++) { - SSL_COMP* comp = sk_SSL_COMP_value(ssl_ctx->comp_methods, i); - if (comp->id != compress_meth) { - continue; - } - const char* name = ((comp->method && comp->method->type == NID_zlib_compression) - ? SN_zlib_compression - : (comp->name ? comp->name : "UNKNOWN")); - JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_compress_meth => %s", ssl_session, name); - return env->NewStringUTF(name); - } - throwSSLExceptionStr(env, "Unknown compression method"); - return NULL; -} - -/** * Frees the SSL session. */ static void NativeCrypto_SSL_SESSION_free(JNIEnv* env, jclass, jint ssl_session_address) { @@ -4224,6 +4398,23 @@ static jint NativeCrypto_d2i_SSL_SESSION(JNIEnv* env, jclass, jbyteArray javaByt 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)); } @@ -4234,6 +4425,7 @@ static JNINativeMethod sNativeCryptoMethods[] = { 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"), @@ -4243,11 +4435,17 @@ static JNINativeMethod sNativeCryptoMethods[] = { NATIVE_METHOD(NativeCrypto, EVP_PKEY_type, "(I)I"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_size, "(I)I"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_free, "(I)V"), + NATIVE_METHOD(NativeCrypto, EVP_PKEY_cmp, "(II)I"), NATIVE_METHOD(NativeCrypto, i2d_PKCS8_PRIV_KEY_INFO, "(I)[B"), NATIVE_METHOD(NativeCrypto, d2i_PKCS8_PRIV_KEY_INFO, "([B)I"), 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"), @@ -4273,6 +4471,7 @@ static JNINativeMethod sNativeCryptoMethods[] = { 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"), @@ -4299,7 +4498,7 @@ static JNINativeMethod sNativeCryptoMethods[] = { NATIVE_METHOD(NativeCrypto, SSL_get_certificate, "(I)[[B"), NATIVE_METHOD(NativeCrypto, SSL_get_peer_cert_chain, "(I)[[B"), NATIVE_METHOD(NativeCrypto, SSL_read, "(I" FILE_DESCRIPTOR SSL_CALLBACKS "[BIII)I"), - NATIVE_METHOD(NativeCrypto, SSL_write, "(I" FILE_DESCRIPTOR SSL_CALLBACKS "[BII)V"), + NATIVE_METHOD(NativeCrypto, SSL_write, "(I" FILE_DESCRIPTOR SSL_CALLBACKS "[BIII)V"), NATIVE_METHOD(NativeCrypto, SSL_interrupt, "(I)V"), NATIVE_METHOD(NativeCrypto, SSL_shutdown, "(I" FILE_DESCRIPTOR SSL_CALLBACKS ")V"), NATIVE_METHOD(NativeCrypto, SSL_free, "(I)V"), @@ -4307,7 +4506,6 @@ static JNINativeMethod sNativeCryptoMethods[] = { NATIVE_METHOD(NativeCrypto, SSL_SESSION_get_time, "(I)J"), NATIVE_METHOD(NativeCrypto, SSL_SESSION_get_version, "(I)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, SSL_SESSION_cipher, "(I)Ljava/lang/String;"), - NATIVE_METHOD(NativeCrypto, SSL_SESSION_compress_meth, "(II)Ljava/lang/String;"), NATIVE_METHOD(NativeCrypto, SSL_SESSION_free, "(I)V"), NATIVE_METHOD(NativeCrypto, i2d_SSL_SESSION, "(I)[B"), NATIVE_METHOD(NativeCrypto, d2i_SSL_SESSION, "([B)I"), 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 @@ LOCAL_SRC_FILES := \ valueOf.cpp LOCAL_C_INCLUDES += \ - external/expat/lib \ external/icu4c/common \ external/icu4c/i18n \ external/openssl/include \ diff --git a/luni/src/main/native/zip.h b/luni/src/main/native/zip.h index 0f3c0c1..303c940 100644 --- a/luni/src/main/native/zip.h +++ b/luni/src/main/native/zip.h @@ -23,7 +23,7 @@ #include "UniquePtr.h" #include "jni.h" #include "zlib.h" -#include "zutil.h" +#include "zutil.h" // For DEF_WBITS and DEF_MEM_LEVEL. static void throwExceptionForZlibError(JNIEnv* env, const char* exceptionClassName, int error) { if (error == Z_MEM_ERROR) { 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 @@ public class ICUTest extends junit.framework.TestCase { 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 @@ public final class DiskLruCacheTest extends TestCase { 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 @@ public final class DiskLruCacheTest extends TestCase { 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 @@ public class MemoryTest extends TestCase { 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/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 @@ public class FileTest extends junit.framework.TestCase { 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 index e973b8f..e46df5d 100644..100755 --- 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.InetSocketAddress; 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; @@ -45,10 +46,16 @@ public final class InterruptedStreamTest extends TestCase { private Socket[] sockets; + @Override protected void setUp() throws Exception { + Thread.interrupted(); // clear interrupted bit + super.tearDown(); + } + @Override protected void tearDown() throws Exception { if (sockets != null) { sockets[0].close(); sockets[1].close(); + sockets = null; } Thread.interrupted(); // clear interrupted bit super.tearDown(); @@ -93,7 +100,7 @@ public final class InterruptedStreamTest extends TestCase { public void testInterruptWritableSocketChannel() throws Exception { sockets = newSocketChannelPair(); - testInterruptReadableChannel(sockets[0].getChannel()); + testInterruptWritableChannel(sockets[0].getChannel()); } /** @@ -110,71 +117,102 @@ public final class InterruptedStreamTest extends TestCase { } private void testInterruptInputStream(final InputStream in) throws Exception { - interruptMeLater(); + Thread thread = interruptMeLater(); try { in.read(); fail(); } catch (InterruptedIOException expected) { + } finally { + waitForInterrupt(thread); } } private void testInterruptReader(final PipedReader reader) throws Exception { - interruptMeLater(); + Thread thread = interruptMeLater(); try { reader.read(); fail(); } catch (InterruptedIOException expected) { + } finally { + waitForInterrupt(thread); } } private void testInterruptReadableChannel(final ReadableByteChannel channel) throws Exception { - interruptMeLater(); + Thread thread = interruptMeLater(); try { channel.read(ByteBuffer.allocate(BUFFER_SIZE)); fail(); } catch (ClosedByInterruptException expected) { + } finally { + waitForInterrupt(thread); } } private void testInterruptOutputStream(final OutputStream out) throws Exception { - interruptMeLater(); + Thread thread = interruptMeLater(); try { // this will block when the receiving buffer fills up while (true) { out.write(new byte[BUFFER_SIZE]); } } catch (InterruptedIOException expected) { + } finally { + waitForInterrupt(thread); } } private void testInterruptWriter(final PipedWriter writer) throws Exception { - interruptMeLater(); + Thread thread = interruptMeLater(); try { // this will block when the receiving buffer fills up while (true) { writer.write(new char[BUFFER_SIZE]); } } catch (InterruptedIOException expected) { + } finally { + waitForInterrupt(thread); } } private void testInterruptWritableChannel(final WritableByteChannel channel) throws Exception { - interruptMeLater(); + Thread thread = interruptMeLater(); try { // this will block when the receiving buffer fills up while (true) { channel.write(ByteBuffer.allocate(BUFFER_SIZE)); } } catch (ClosedByInterruptException expected) { + } catch (ClosedChannelException expected) { + } finally { + waitForInterrupt(thread); } } - private void interruptMeLater() throws Exception { + private Thread interruptMeLater() throws Exception { final Thread toInterrupt = Thread.currentThread(); - new Thread(new Runnable () { + Thread thread = new Thread(new Runnable () { @Override public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + } toInterrupt.interrupt(); } - }).start(); + }); + thread.start(); + return thread; + } + + private static void waitForInterrupt(Thread thread) throws Exception { + try { + thread.join(); + } catch (InterruptedException ignore) { + // There is currently a race between Thread.interrupt in + // interruptMeLater and Thread.join here. Most of the time + // we won't get an InterruptedException, but occasionally + // we do, so for now ignore this exception. + // http://b/6951157 + } } } 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 @@ package libcore.java.io; 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 @@ public final class SerializationTest extends TestCase { // 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 @@ public final class SerializationTest extends TestCase { 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 75a4e42..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 @@ package libcore.java.lang; 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); diff --git a/luni/src/test/java/libcore/java/lang/OldObjectTest.java b/luni/src/test/java/libcore/java/lang/OldObjectTest.java index 08471b2..3ab0327 100644 --- a/luni/src/test/java/libcore/java/lang/OldObjectTest.java +++ b/luni/src/test/java/libcore/java/lang/OldObjectTest.java @@ -16,8 +16,6 @@ */ package libcore.java.lang; -import dalvik.annotation.SideEffect; -import java.util.Vector; import junit.framework.TestCase; public class OldObjectTest extends TestCase { @@ -187,7 +185,36 @@ public class OldObjectTest extends TestCase { fail("InterruptedException was thrown."); } assertEquals(3, status); + } + + public void test_waitJI_invalid() throws Exception { + Object o = new Object(); + synchronized (o) { + try { + o.wait(-1, 0); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + o.wait(0, -1); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + o.wait(-1, -1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // The ms timeout must fit in 32 bits. + try { + o.wait(Integer.MAX_VALUE + 1, 0); + fail(); + } catch (IllegalArgumentException expected) { + } + } } public void test_waitJ() { 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 class StringTest extends TestCase { } } - 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 @@ public final class FinalizeTest extends TestCase { 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/ConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java index d33c5f3..99a479c2 100644 --- a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java +++ b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java @@ -16,11 +16,13 @@ package libcore.java.net; +import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketAddress; import java.net.SocketException; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.ClosedChannelException; @@ -36,25 +38,25 @@ import tests.net.StuckServer; */ public class ConcurrentCloseTest extends junit.framework.TestCase { public void test_accept() throws Exception { - ServerSocket s = new ServerSocket(0); - new Killer(s).start(); + ServerSocket ss = new ServerSocket(0); + new Killer(ss).start(); try { System.err.println("accept..."); - s.accept(); - fail("accept returned!"); + Socket s = ss.accept(); + fail("accept returned " + s + "!"); } catch (SocketException expected) { assertEquals("Socket closed", expected.getMessage()); } } public void test_connect() throws Exception { - StuckServer ss = new StuckServer(); + StuckServer ss = new StuckServer(false); Socket s = new Socket(); new Killer(s).start(); try { System.err.println("connect..."); s.connect(ss.getLocalSocketAddress()); - fail("connect returned!"); + fail("connect returned: " + s + "!"); } catch (SocketException expected) { assertEquals("Socket closed", expected.getMessage()); } finally { @@ -63,13 +65,13 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { } public void test_connect_timeout() throws Exception { - StuckServer ss = new StuckServer(); + StuckServer ss = new StuckServer(false); Socket s = new Socket(); new Killer(s).start(); try { System.err.println("connect (with timeout)..."); s.connect(ss.getLocalSocketAddress(), 3600 * 1000); - fail("connect returned!"); + fail("connect returned: " + s + "!"); } catch (SocketException expected) { assertEquals("Socket closed", expected.getMessage()); } finally { @@ -78,7 +80,7 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { } public void test_connect_nonBlocking() throws Exception { - StuckServer ss = new StuckServer(); + StuckServer ss = new StuckServer(false); SocketChannel s = SocketChannel.open(); new Killer(s.socket()).start(); try { @@ -88,7 +90,7 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { while (!s.finishConnect()) { // Spin like a mad thing! } - fail("connect returned!"); + fail("connect returned: " + s + "!"); } catch (SocketException expected) { assertEquals("Socket closed", expected.getMessage()); } catch (AsynchronousCloseException alsoOkay) { @@ -102,23 +104,14 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { } public void test_read() throws Exception { - final ServerSocket ss = new ServerSocket(0); - new Thread(new Runnable() { - public void run() { - try { - ss.accept(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - }).start(); + SilentServer ss = new SilentServer(); Socket s = new Socket(); s.connect(ss.getLocalSocketAddress()); new Killer(s).start(); try { System.err.println("read..."); int i = s.getInputStream().read(); - fail("read returned " + i); + fail("read returned: " + i); } catch (SocketException expected) { assertEquals("Socket closed", expected.getMessage()); } @@ -126,16 +119,7 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { } public void test_read_multiple() throws Throwable { - final ServerSocket ss = new ServerSocket(0); - new Thread(new Runnable() { - public void run() { - try { - ss.accept(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - }).start(); + SilentServer ss = new SilentServer(); final Socket s = new Socket(); s.connect(ss.getLocalSocketAddress()); @@ -152,7 +136,7 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { try { System.err.println("read..."); int i = s.getInputStream().read(); - fail("read returned " + i); + fail("read returned: " + i); } catch (SocketException expected) { assertEquals("Socket closed", expected.getMessage()); } @@ -173,6 +157,8 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { for (Throwable exception : thrownExceptions) { throw exception; } + + ss.close(); } public void test_recv() throws Exception { @@ -190,21 +176,7 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { } public void test_write() throws Exception { - final ServerSocket ss = new ServerSocket(0); - new Thread(new Runnable() { - public void run() { - try { - System.err.println("accepting..."); - - Socket client = ss.accept(); - System.err.println("accepted..."); - Thread.sleep(30 * 1000); - System.err.println("server exiting..."); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - }).start(); + final SilentServer ss = new SilentServer(); Socket s = new Socket(); s.connect(ss.getLocalSocketAddress()); new Killer(s).start(); @@ -224,6 +196,37 @@ public class ConcurrentCloseTest extends junit.framework.TestCase { ss.close(); } + // This server accepts connections, but doesn't read or write anything. + // It holds on to the Socket connecting to the client so it won't be GCed. + // Call "close" to close both the server socket and its client connection. + static class SilentServer { + private final ServerSocket ss; + private Socket client; + + public SilentServer() throws IOException { + ss = new ServerSocket(0); + new Thread(new Runnable() { + public void run() { + try { + client = ss.accept(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }).start(); + } + + public SocketAddress getLocalSocketAddress() { + return ss.getLocalSocketAddress(); + } + + public void close() throws IOException { + client.close(); + ss.close(); + } + } + + // This thread calls the "close" method on the supplied T after 2s. static class Killer<T> extends Thread { private final T s; 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.io.IOException; 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 class OldDatagramPacketTest extends junit.framework.TestCase { } 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 class OldDatagramPacketTest extends junit.framework.TestCase { } 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.IllegalBlockingModeException; 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 class OldServerSocketTest extends OldSocketTestCase { } 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 class OldServerSocketTest extends OldSocketTestCase { } 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 class OldServerSocketTest extends OldSocketTestCase { } 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 class OldServerSocketTest extends OldSocketTestCase { } 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/OldSocketTest.java b/luni/src/test/java/libcore/java/net/OldSocketTest.java index fda9557..033a7bf 100644 --- a/luni/src/test/java/libcore/java/net/OldSocketTest.java +++ b/luni/src/test/java/libcore/java/net/OldSocketTest.java @@ -38,7 +38,6 @@ import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.SocketChannel; import java.security.Permission; import tests.support.Support_Configuration; -import tests.support.Support_PortManager; public class OldSocketTest extends OldSocketTestCase { @@ -115,17 +114,15 @@ public class OldSocketTest extends OldSocketTestCase { public void test_ConstructorLjava_lang_StringILjava_net_InetAddressI1() throws IOException { int sport = startServer("Cons String,I,InetAddress,I"); - int portNumber = Support_PortManager.getNextPort(); s = new Socket(InetAddress.getLocalHost().getHostName(), sport, - InetAddress.getLocalHost(), portNumber); + InetAddress.getLocalHost(), 0); assertTrue("Failed to create socket", s.getPort() == sport); } public void test_ConstructorLjava_lang_StringILjava_net_InetAddressI2() throws IOException { - int testPort = Support_PortManager.getNextPort(); - Socket s1 = new Socket("www.google.com", 80, null, testPort); + Socket s1 = new Socket("www.google.com", 80, null, 0); try { - Socket s2 = new Socket("www.google.com", 80, null, testPort); + Socket s2 = new Socket("www.google.com", 80, null, s1.getLocalPort()); try { s2.close(); } catch (IOException ignored) { @@ -162,10 +159,8 @@ public class OldSocketTest extends OldSocketTestCase { // Test for method java.net.Socket(java.net.InetAddress, int, // java.net.InetAddress, int) int sport = startServer("Cons InetAddress,I,InetAddress,I"); - int portNumber = Support_PortManager.getNextPort(); s = new Socket(InetAddress.getLocalHost().getHostName(), sport, - InetAddress.getLocalHost(), portNumber); - assertTrue("Failed to create socket", s.getLocalPort() == portNumber); + InetAddress.getLocalHost(), 0); } public void test_ConstructorLjava_net_InetAddressIZ() throws IOException { @@ -180,8 +175,7 @@ public class OldSocketTest extends OldSocketTestCase { public void test_close() throws IOException { // Test for method void java.net.Socket.close() int sport = startServer("SServer close"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); try { s.setSoLinger(false, 100); } catch (IOException e) { @@ -199,8 +193,7 @@ public class OldSocketTest extends OldSocketTestCase { public void test_getInetAddress() throws IOException { // Test for method java.net.InetAddress java.net.Socket.getInetAddress() int sport = startServer("SServer getInetAddress"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); assertTrue("Returned incorrect InetAddress", s.getInetAddress().equals( InetAddress.getLocalHost())); @@ -220,9 +213,7 @@ public class OldSocketTest extends OldSocketTestCase { public void test_getKeepAlive() { try { int sport = startServer("SServer getKeepAlive"); - int portNumber = Support_PortManager.getNextPort(); - Socket theSocket = new Socket(InetAddress.getLocalHost(), sport, - null, portNumber); + Socket theSocket = new Socket(InetAddress.getLocalHost(), sport, null, 0); theSocket.setKeepAlive(true); assertTrue("getKeepAlive false when it should be true", theSocket .getKeepAlive()); @@ -254,8 +245,7 @@ public class OldSocketTest extends OldSocketTestCase { // Test for method java.net.InetAddress // java.net.Socket.getLocalAddress() int sport = startServer("SServer getLocAddress"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); assertEquals("Returned incorrect InetAddress", InetAddress.getLocalHost(), s.getLocalAddress()); @@ -271,10 +261,10 @@ public class OldSocketTest extends OldSocketTestCase { public void test_getLocalPort() throws IOException { // Test for method int java.net.Socket.getLocalPort() int sport = startServer("SServer getLocalPort"); - int portNumber = Support_PortManager.getNextPort(); s = new Socket(InetAddress.getLocalHost().getHostName(), sport, - InetAddress.getLocalHost(), portNumber); - assertTrue("Returned incorrect port", s.getLocalPort() == portNumber); + InetAddress.getLocalHost(), 0); + // There's nothing we can usefully assert about the kernel-assigned port. + s.getLocalPort(); } @SuppressWarnings("deprecation") @@ -282,15 +272,13 @@ public class OldSocketTest extends OldSocketTestCase { // Test for method java.io.OutputStream // java.net.Socket.getOutputStream() int sport = startServer("SServer getOutputStream"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport); java.io.OutputStream os = s.getOutputStream(); assertNotNull("Failed to get stream", os); os.write(1); s.close(); // Regression test for harmony-2934 - s = new Socket("127.0.0.1", Support_PortManager.getNextPort(), - false); + s = new Socket("127.0.0.1", sport, false); OutputStream o = s.getOutputStream(); o.write(1); try { @@ -301,8 +289,7 @@ public class OldSocketTest extends OldSocketTestCase { s.close(); // Regression test for harmony-2942 - s = new Socket("0.0.0.0", Support_PortManager.getNextPort(), - false); + s = new Socket("0.0.0.0", sport, false); o = s.getOutputStream(); o.write(1); try { @@ -316,18 +303,15 @@ public class OldSocketTest extends OldSocketTestCase { public void test_getPort() throws IOException { // Test for method int java.net.Socket.getPort() int sport = startServer("SServer getPort"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); - assertTrue("Returned incorrect port" + s.getPort(), - s.getPort() == sport); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); + assertTrue("Returned incorrect port" + s.getPort(), s.getPort() == sport); } public void test_getSoLinger() { // Test for method int java.net.Socket.getSoLinger() int sport = startServer("SServer getSoLinger"); try { - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); s.setSoLinger(true, 200); assertEquals("Returned incorrect linger", 200, s.getSoLinger()); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_LINGER); @@ -337,8 +321,7 @@ public class OldSocketTest extends OldSocketTestCase { } try { - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); s.close(); try { s.getSoLinger(); @@ -354,9 +337,7 @@ public class OldSocketTest extends OldSocketTestCase { public void test_getReceiveBufferSize() { try { int sport = startServer("SServer getReceiveBufferSize"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost().getHostName(), sport, - null, portNumber); + s = new Socket(InetAddress.getLocalHost().getHostName(), sport, null, 0); s.setReceiveBufferSize(130); assertTrue("Incorrect buffer size", s.getReceiveBufferSize() >= 130); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_RCVBUF); @@ -381,9 +362,7 @@ public class OldSocketTest extends OldSocketTestCase { public void test_getSendBufferSize() { int sport = startServer("SServer setSendBufferSize"); try { - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost().getHostName(), sport, - null, portNumber); + s = new Socket(InetAddress.getLocalHost().getHostName(), sport, null, 0); s.setSendBufferSize(134); assertTrue("Incorrect buffer size", s.getSendBufferSize() >= 134); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_SNDBUF); @@ -391,8 +370,7 @@ public class OldSocketTest extends OldSocketTestCase { handleException(e, SO_SNDBUF); } try { - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); s.close(); try { s.getSendBufferSize(); @@ -430,8 +408,7 @@ public class OldSocketTest extends OldSocketTestCase { // Test for method boolean java.net.Socket.getTcpNoDelay() int sport = startServer("SServer getTcpNoDelay"); try { - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); boolean bool = !s.getTcpNoDelay(); s.setTcpNoDelay(bool); assertTrue("Failed to get no delay setting: " + s.getTcpNoDelay(), @@ -442,8 +419,7 @@ public class OldSocketTest extends OldSocketTestCase { } try { - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); s.close(); try { s.getTcpNoDelay(); @@ -461,9 +437,7 @@ public class OldSocketTest extends OldSocketTestCase { // crashed machines. Just make sure we can set it try { int sport = startServer("SServer setKeepAlive"); - int portNumber = Support_PortManager.getNextPort(); - Socket theSocket = new Socket(InetAddress.getLocalHost(), sport, - null, portNumber); + Socket theSocket = new Socket(InetAddress.getLocalHost(), sport, null, 0); theSocket.setKeepAlive(true); theSocket.setKeepAlive(false); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_KEEPALIVE); @@ -509,8 +483,7 @@ public class OldSocketTest extends OldSocketTestCase { public void test_setSendBufferSizeI() { try { int sport = startServer("SServer setSendBufferSizeI"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); s.setSendBufferSize(134); assertTrue("Incorrect buffer size", s.getSendBufferSize() >= 134); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_SNDBUF); @@ -533,8 +506,7 @@ public class OldSocketTest extends OldSocketTestCase { public void test_setReceiveBufferSizeI() { try { int sport = startServer("SServer setReceiveBufferSizeI"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); s.setReceiveBufferSize(130); assertTrue("Incorrect buffer size", s.getReceiveBufferSize() >= 130); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_RCVBUF); @@ -558,8 +530,7 @@ public class OldSocketTest extends OldSocketTestCase { // Test for method void java.net.Socket.setSoLinger(boolean, int) try { int sport = startServer("SServer setSoLingerZI"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); s.setSoLinger(true, 500); assertEquals("Set incorrect linger", 500, s.getSoLinger()); ensureExceptionThrownIfOptionIsUnsupportedOnOS(SO_LINGER); @@ -584,8 +555,7 @@ public class OldSocketTest extends OldSocketTestCase { // Test for method void java.net.Socket.setTcpNoDelay(boolean) try { int sport = startServer("SServer setTcpNoDelayZ"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); boolean bool; s.setTcpNoDelay(bool = !s.getTcpNoDelay()); assertTrue("Failed to set no delay setting: " + s.getTcpNoDelay(), @@ -610,9 +580,8 @@ public class OldSocketTest extends OldSocketTestCase { public void test_toString() throws IOException { // Test for method java.lang.String java.net.Socket.toString() int sport = startServer("SServer toString"); - int portNumber = Support_PortManager.getNextPort(); s = new Socket(InetAddress.getLocalHost().getHostName(), sport, - InetAddress.getLocalHost(), portNumber); + InetAddress.getLocalHost(), 0); assertEquals("Socket[address=" + InetAddress.getLocalHost() + ",port=" + s.getPort() + ",localPort=" + s.getLocalPort() + "]", s.toString()); } @@ -620,9 +589,8 @@ public class OldSocketTest extends OldSocketTestCase { // AndroidOnly: RI returns wrong value for EOF public void test_shutdownInput() throws Exception { InetAddress addr = InetAddress.getLocalHost(); - int port = Support_PortManager.getNextPort(); - ServerSocket serverSocket = new ServerSocket(port, 5, addr); - Socket theSocket = new Socket(addr, port); + ServerSocket serverSocket = new ServerSocket(0, 5, addr); + Socket theSocket = new Socket(addr, serverSocket.getLocalPort()); Socket servSock = serverSocket.accept(); InputStream theInput = theSocket.getInputStream(); @@ -656,10 +624,8 @@ public class OldSocketTest extends OldSocketTestCase { } public void test_shutdownOutput() throws IOException { - InetAddress addr = InetAddress.getLocalHost(); - int port = Support_PortManager.getNextPort(); - ServerSocket serverSocket = new ServerSocket(port, 5, addr); - Socket theSocket = new Socket(addr, port); + ServerSocket serverSocket = new ServerSocket(0, 5); + Socket theSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); Socket servSock = serverSocket.accept(); InputStream theInput = theSocket.getInputStream(); @@ -692,17 +658,9 @@ public class OldSocketTest extends OldSocketTestCase { // set up server connect and then validate that we get the right // response for the local address int sport = startServer("SServer getLocSocketAddress"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); - assertTrue( - "Returned incorrect InetSocketAddress(1):" - + s.getLocalSocketAddress().toString() - + "Expected: " - + (new InetSocketAddress(InetAddress.getLocalHost(), - portNumber)).toString(), s - .getLocalSocketAddress().equals( - new InetSocketAddress(InetAddress - .getLocalHost(), portNumber))); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); + assertEquals(new InetSocketAddress(InetAddress.getLocalHost(), s.getLocalPort()), + s.getLocalSocketAddress()); s.close(); // now create a socket that is not bound and validate we get the @@ -713,21 +671,12 @@ public class OldSocketTest extends OldSocketTestCase { theSocket.getLocalSocketAddress()); // now bind the socket and make sure we get the right answer - portNumber = Support_PortManager.getNextPort(); - theSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), - portNumber)); - assertTrue( - "Returned incorrect InetSocketAddress(2):" - + theSocket.getLocalSocketAddress().toString() - + "Expected: " - + (new InetSocketAddress(InetAddress.getLocalHost(), - portNumber)).toString(), theSocket - .getLocalSocketAddress().equals( - new InetSocketAddress(InetAddress - .getLocalHost(), portNumber))); + theSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0)); + assertEquals(new InetSocketAddress(InetAddress.getLocalHost(), theSocket.getLocalPort()), + theSocket.getLocalSocketAddress()); theSocket.close(); - // now validate that behaviour when the any address is returned + // now validate that behavior when the any address is returned s = new Socket(); s.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0)); @@ -747,8 +696,7 @@ public class OldSocketTest extends OldSocketTestCase { // set up server connect and then validate that we get the right // response for the remote address int sport = startServer("SServer getLocRemoteAddress"); - int portNumber = Support_PortManager.getNextPort(); - s = new Socket(InetAddress.getLocalHost(), sport, null, portNumber); + s = new Socket(InetAddress.getLocalHost(), sport, null, 0); assertTrue("Returned incorrect InetSocketAddress(1):" + s.getLocalSocketAddress().toString(), s.getRemoteSocketAddress() @@ -760,9 +708,7 @@ public class OldSocketTest extends OldSocketTestCase { // now create one that is not connect and validate that we get the // right answer Socket theSocket = new Socket(); - portNumber = Support_PortManager.getNextPort(); - theSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), - portNumber)); + theSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0)); assertNull("Returned incorrect InetSocketAddress -unconnected socket:" + "Expected: NULL", theSocket.getRemoteSocketAddress()); @@ -781,10 +727,8 @@ public class OldSocketTest extends OldSocketTestCase { } public void test_isBound() throws IOException { - InetAddress addr = InetAddress.getLocalHost(); - int port = Support_PortManager.getNextPort(); - ServerSocket serverSocket = new ServerSocket(port, 5, addr); - Socket theSocket = new Socket(addr, port); + ServerSocket serverSocket = new ServerSocket(0, 5); + Socket theSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); Socket servSock = serverSocket.accept(); assertTrue("Socket indicated not bound when it should be (1)", theSocket.isBound()); @@ -793,14 +737,11 @@ public class OldSocketTest extends OldSocketTestCase { // now do it with the new constructors and revalidate. Connect causes // the socket to be bound - InetSocketAddress theAddress = new InetSocketAddress(InetAddress - .getLocalHost(), Support_PortManager.getNextPort()); theSocket = new Socket(); assertFalse("Socket indicated bound when it was not (2)", theSocket .isBound()); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); servSock = serverSocket.accept(); assertTrue("Socket indicated not bound when it should be (2)", theSocket.isBound()); @@ -808,12 +749,10 @@ public class OldSocketTest extends OldSocketTestCase { serverSocket.close(); // now test when we bind explicitly - InetSocketAddress theLocalAddress = new InetSocketAddress(InetAddress - .getLocalHost(), Support_PortManager.getNextPort()); theSocket = new Socket(); assertFalse("Socket indicated bound when it was not (3)", theSocket .isBound()); - theSocket.bind(theLocalAddress); + theSocket.bind(null); assertTrue("Socket indicated not bound when it should be (3a)", theSocket.isBound()); theSocket.close(); @@ -822,10 +761,8 @@ public class OldSocketTest extends OldSocketTestCase { } public void test_isConnected() throws IOException { - InetAddress addr = InetAddress.getLocalHost(); - int port = Support_PortManager.getNextPort(); - ServerSocket serverSocket = new ServerSocket(port, 5, addr); - Socket theSocket = new Socket(addr, port); + ServerSocket serverSocket = new ServerSocket(0, 5); + Socket theSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); Socket servSock = serverSocket.accept(); assertTrue("Socket indicated not connected when it should be", theSocket.isConnected()); @@ -833,14 +770,11 @@ public class OldSocketTest extends OldSocketTestCase { serverSocket.close(); // now do it with the new constructors and revalidate - InetSocketAddress theAddress = new InetSocketAddress(InetAddress - .getLocalHost(), Support_PortManager.getNextPort()); theSocket = new Socket(); assertFalse("Socket indicated connected when it was not", theSocket .isConnected()); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); servSock = serverSocket.accept(); assertTrue("Socket indicated not connected when it should be", theSocket.isConnected()); @@ -849,10 +783,8 @@ public class OldSocketTest extends OldSocketTestCase { } public void test_isClosed() throws IOException { - InetAddress addr = InetAddress.getLocalHost(); - int port = Support_PortManager.getNextPort(); - ServerSocket serverSocket = new ServerSocket(port, 5, addr); - Socket theSocket = new Socket(addr, port); + ServerSocket serverSocket = new ServerSocket(0, 5); + Socket theSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); Socket servSock = serverSocket.accept(); // validate isClosed returns expected values @@ -862,7 +794,7 @@ public class OldSocketTest extends OldSocketTestCase { assertTrue("Socket should indicate it is closed(1):", theSocket .isClosed()); - theSocket = new Socket(addr, port); + theSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); assertFalse("Socket should indicate it is not closed(2):", theSocket .isClosed()); theSocket.close(); @@ -891,7 +823,7 @@ public class OldSocketTest extends OldSocketTestCase { try { theSocket.bind(new InetSocketAddress(InetAddress .getByAddress(Support_Configuration.nonLocalAddressBytes), - Support_PortManager.getNextPort())); + 80)); fail("No exception when binding to bad address:" + theSocket.getLocalSocketAddress().toString()); } catch (IOException ex) { @@ -900,39 +832,21 @@ public class OldSocketTest extends OldSocketTestCase { // now create a socket that is not bound and then bind it theSocket = new Socket(); - int portNumber = Support_PortManager.getNextPort(); theSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), - portNumber)); + 0)); // validate that the localSocketAddress reflects the address we // bound to - assertTrue( - "Local address not correct after bind:" - + theSocket.getLocalSocketAddress().toString() - + " Expected: " - + (new InetSocketAddress(InetAddress.getLocalHost(), - portNumber)).toString(), theSocket - .getLocalSocketAddress().equals( - new InetSocketAddress(InetAddress - .getLocalHost(), portNumber))); + assertEquals(new InetSocketAddress(InetAddress.getLocalHost(), theSocket.getLocalPort()), + theSocket.getLocalSocketAddress()); // make sure we can now connect and that connections appear to come // from the address we bound to. - InetSocketAddress theAddress = new InetSocketAddress(InetAddress - .getLocalHost(), Support_PortManager.getNextPort()); - ServerSocket serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + ServerSocket serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); Socket servSock = serverSocket.accept(); - assertTrue( - "Returned Remote address from server connected to does not match expected local address:" - + servSock.getRemoteSocketAddress().toString() - + " Expected: " - + (new InetSocketAddress(InetAddress.getLocalHost(), - portNumber)).toString(), servSock - .getRemoteSocketAddress().equals( - new InetSocketAddress(InetAddress - .getLocalHost(), portNumber))); + assertEquals(new InetSocketAddress(InetAddress.getLocalHost(), theSocket.getLocalPort()), + servSock.getRemoteSocketAddress()); theSocket.close(); servSock.close(); serverSocket.close(); @@ -951,10 +865,8 @@ public class OldSocketTest extends OldSocketTestCase { theSocket = new Socket(); Socket theSocket2 = new Socket(); try { - theAddress = new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); - theSocket.bind(theAddress); - theSocket2.bind(theAddress); + theSocket.bind(null); + theSocket2.bind(theSocket.getLocalSocketAddress()); fail("No exception binding to address that is not available"); } catch (IOException ex) { } @@ -1020,7 +932,7 @@ public class OldSocketTest extends OldSocketTestCase { } // start by validating the error checks - int portNumber = Support_PortManager.getNextPort(); + int portNumber = 0; Socket theSocket = null; ServerSocket serverSocket = null; SocketAddress theAddress = null; @@ -1084,17 +996,8 @@ public class OldSocketTest extends OldSocketTestCase { // now validate that we can actually connect when somebody is listening theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); - theSocket.close(); - serverSocket.close(); - - // now validate that we can actually connect when somebody is listening - theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); // validate that when a socket is connected that it answers // correctly to related queries @@ -1119,10 +1022,9 @@ public class OldSocketTest extends OldSocketTestCase { // are already connected try { theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); + theSocket.connect(serverSocket.getLocalSocketAddress()); theSocket.close(); serverSocket.close(); fail("No exception when we try to connect on a connected socket: "); @@ -1145,9 +1047,8 @@ public class OldSocketTest extends OldSocketTestCase { // now validate that connected socket can be used to read/write theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); Socket servSock = serverSocket.accept(); InputStream theInput = theSocket.getInputStream(); OutputStream theOutput = servSock.getOutputStream(); @@ -1197,10 +1098,8 @@ public class OldSocketTest extends OldSocketTestCase { SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); Socket socket = channel.socket(); - int port = Support_PortManager.getNextPort(); try { - socket.connect( new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort())); + socket.connect(serverSocket.getLocalSocketAddress()); fail("IllegalBlockingModeException was not thrown."); } catch (IllegalBlockingModeException expected) { } @@ -1263,28 +1162,14 @@ public class OldSocketTest extends OldSocketTestCase { } // start by validating the error checks - int portNumber = Support_PortManager.getNextPort(); - Socket theSocket = null; - ServerSocket serverSocket = null; - SocketAddress theAddress = null; - SocketAddress nonConnectableAddress = null; - SocketAddress nonReachableAddress = null; - SocketAddress nonListeningAddress = null; - SocketAddress invalidType = null; byte[] theBytes = { 0, 0, 0, 0 }; + SocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0); + SocketAddress nonConnectableAddress = new InetSocketAddress(InetAddress.getByAddress(theBytes), 0); + SocketAddress nonReachableAddress = new InetSocketAddress(InetAddress.getByName(Support_Configuration.ResolvedNotExistingHost), 0); + SocketAddress invalidType = new mySocketAddress(); - theAddress = new InetSocketAddress(InetAddress.getLocalHost(), - portNumber); - nonConnectableAddress = new InetSocketAddress(InetAddress - .getByAddress(theBytes), portNumber); - nonReachableAddress = new InetSocketAddress(InetAddress - .getByName(Support_Configuration.ResolvedNotExistingHost), - portNumber); - // make sure we get another port - Thread.sleep(7000); - nonListeningAddress = new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); - invalidType = new mySocketAddress(); + Socket theSocket = null; + ServerSocket serverSocket = null; try { theSocket = new Socket(); @@ -1340,9 +1225,8 @@ public class OldSocketTest extends OldSocketTestCase { // now validate that we can actually connect when somebody is listening theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress, 0); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); theSocket.close(); serverSocket.close(); @@ -1350,7 +1234,7 @@ public class OldSocketTest extends OldSocketTestCase { // an address on which nobody is listening try { theSocket = new Socket(); - theSocket.connect(nonListeningAddress, 100000); + theSocket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 80), 100000); theSocket.close(); fail("No exception when connecting to address nobody listening on: "); } catch (Exception e) { @@ -1390,12 +1274,9 @@ public class OldSocketTest extends OldSocketTestCase { } // now validate that we can actually connect when somebody is listening - new InetSocketAddress(InetAddress.getLocalHost(), Support_PortManager - .getNextPort()); theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress, 100000); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); // validate that when a socket is connected that it answers // correctly to related queries @@ -1419,8 +1300,6 @@ public class OldSocketTest extends OldSocketTestCase { // now validate that we get the right exception if we connect when we // are already connected try { - new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); theSocket = new Socket(); serverSocket = new ServerSocket(); serverSocket.bind(theAddress); @@ -1447,12 +1326,9 @@ public class OldSocketTest extends OldSocketTestCase { } // now validate that connected socket can be used to read/write - new InetSocketAddress(InetAddress.getLocalHost(), Support_PortManager - .getNextPort()); theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress, 100000); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); Socket servSock = serverSocket.accept(); InputStream theInput = theSocket.getInputStream(); OutputStream theOutput = servSock.getOutputStream(); @@ -1515,10 +1391,8 @@ public class OldSocketTest extends OldSocketTestCase { SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); Socket socket = channel.socket(); - int port = Support_PortManager.getNextPort(); try { - socket.connect( new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()), port); + socket.connect(serverSocket.getLocalSocketAddress()); fail("IllegalBlockingModeException was not thrown."); } catch (IllegalBlockingModeException expected) { } @@ -1526,12 +1400,9 @@ public class OldSocketTest extends OldSocketTestCase { } public void test_isInputShutdown() throws IOException { - InetSocketAddress theAddress = new InetSocketAddress(InetAddress - .getLocalHost(), Support_PortManager.getNextPort()); Socket theSocket = new Socket(); - ServerSocket serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + ServerSocket serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); Socket servSock = serverSocket.accept(); InputStream theInput = theSocket.getInputStream(); OutputStream theOutput = servSock.getOutputStream(); @@ -1559,12 +1430,9 @@ public class OldSocketTest extends OldSocketTestCase { } public void test_isOutputShutdown() throws IOException { - InetSocketAddress theAddress = new InetSocketAddress(InetAddress - .getLocalHost(), Support_PortManager.getNextPort()); Socket theSocket = new Socket(); - ServerSocket serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + ServerSocket serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); Socket servSock = serverSocket.accept(); InputStream theInput = theSocket.getInputStream(); OutputStream theOutput = servSock.getOutputStream(); @@ -1591,18 +1459,14 @@ public class OldSocketTest extends OldSocketTestCase { } - public void test_setReuseAddressZ() { + public void test_setReuseAddressZ() throws Exception { try { InetAddress allAddresses[] = InetAddress.getAllByName(InetAddress .getLocalHost().getHostName()); if (allAddresses.length > 1) { - InetSocketAddress theAddress = new InetSocketAddress( - InetAddress.getLocalHost(), Support_PortManager - .getNextPort()); - ServerSocket serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); + ServerSocket serverSocket = new ServerSocket(0, 5); // try to bind to port address that is already in use with // reuseAddress = false. @@ -1612,17 +1476,15 @@ public class OldSocketTest extends OldSocketTestCase { // what the expected result is. It seems that on linux // platforms we also don't get an exception. InetSocketAddress theLocalAddress = new InetSocketAddress( - (InetAddress) allAddresses[1], Support_PortManager - .getNextPort()); + (InetAddress) allAddresses[1], 0); InetSocketAddress theOtherLocalAddress = new InetSocketAddress( - (InetAddress) allAddresses[0], theLocalAddress - .getPort()); + (InetAddress) allAddresses[0], theLocalAddress.getPort()); Socket theSocket = new Socket(); theSocket.setReuseAddress(false); theSocket.bind(theLocalAddress); Socket theSocket2 = null; String platform = System.getProperty("os.name"); - try { + theSocket2 = new Socket(); theSocket2.setReuseAddress(false); theSocket2.bind(theOtherLocalAddress); @@ -1639,69 +1501,34 @@ public class OldSocketTest extends OldSocketTestCase { + ":" + theOtherLocalAddress.toString()); } - } catch (IOException ex) { - if ((platform.startsWith("Linux")) - || ((platform.startsWith("Windows")) && ((((InetAddress) allAddresses[0]) instanceof Inet4Address) && (((InetAddress) allAddresses[1]) instanceof Inet4Address)))) { - fail("Got unexpected exception when binding with setReuseAddress false on windows platform:" - + theAddress.toString() + ":" + ex.toString()); - } - } theSocket.close(); theSocket2.close(); // try to bind to port that is already in use with reuseAddress // = true - theLocalAddress = new InetSocketAddress( - (InetAddress) allAddresses[0], Support_PortManager - .getNextPort()); - theOtherLocalAddress = new InetSocketAddress( - (InetAddress) allAddresses[1], theLocalAddress - .getPort()); + theLocalAddress = new InetSocketAddress((InetAddress) allAddresses[0], 0); theSocket = new Socket(); theSocket.setReuseAddress(true); theSocket.bind(theLocalAddress); - try { - theSocket2 = new Socket(); - theSocket2.setReuseAddress(true); - theSocket2.bind(theOtherLocalAddress); - theSocket2.close(); - } catch (IOException ex) { - fail("IOException when setReuseAddress is true and we bind :" - + ex.toString()); - } + theSocket2 = new Socket(); + theSocket2.setReuseAddress(true); + theOtherLocalAddress = new InetSocketAddress((InetAddress) allAddresses[1], theSocket.getLocalPort()); + theSocket2.bind(theOtherLocalAddress); + theSocket2.close(); theSocket.close(); serverSocket.close(); // try with default behavior which should be the same on all // platforms - theLocalAddress = new InetSocketAddress( - (InetAddress) allAddresses[0], Support_PortManager - .getNextPort()); - theOtherLocalAddress = new InetSocketAddress( - (InetAddress) allAddresses[1], theLocalAddress - .getPort()); + theLocalAddress = new InetSocketAddress((InetAddress) allAddresses[0], 0); theSocket = new Socket(); theSocket.bind(theLocalAddress); - try { - theSocket2 = new Socket(); - theSocket2.bind(theOtherLocalAddress); - theSocket2.close(); - if ((!platform.startsWith("Linux")) - && ((!platform.startsWith("Windows")) || !((((InetAddress) allAddresses[0]) instanceof Inet4Address) && (((InetAddress) allAddresses[1]) instanceof Inet4Address)))) { - fail("No exception when setReuseAddress is default and we bind:" - + theLocalAddress.toString() - + ":" - + theOtherLocalAddress.toString()); - } - } catch (IOException ex) { - if ((platform.startsWith("Linux")) - || ((platform.startsWith("Windows")) && ((((InetAddress) allAddresses[0]) instanceof Inet4Address) && (((InetAddress) allAddresses[1]) instanceof Inet4Address)))) { - fail("Got unexpected exception when binding with setReuseAddress default on windows platform:" - + theAddress.toString() + ":" + ex.toString()); - } - } + theSocket2 = new Socket(); + theOtherLocalAddress = new InetSocketAddress((InetAddress) allAddresses[1], theSocket.getLocalPort()); + theSocket2.bind(theOtherLocalAddress); + theSocket2.close(); theSocket.close(); serverSocket.close(); @@ -1754,8 +1581,6 @@ public class OldSocketTest extends OldSocketTestCase { public void test_setOOBInlineZ() { // mostly tested in getOOBInline. Just set to make sure call works ok try { - new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); Socket theSocket = new Socket(); theSocket.setOOBInline(true); assertTrue("expected OOBIline to be true", theSocket.getOOBInline()); @@ -1779,8 +1604,6 @@ public class OldSocketTest extends OldSocketTestCase { public void test_getOOBInline() { try { - new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); Socket theSocket = new Socket(); // validate that value reflects what we set it to true after true, @@ -1812,8 +1635,6 @@ public class OldSocketTest extends OldSocketTestCase { int IPTOS_LOWCOST = 0x2; int IPTOS_THROUGHPUT = 0x8; - new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); Socket theSocket = new Socket(); // validate that value set must be between 0 and 255 @@ -1851,8 +1672,6 @@ public class OldSocketTest extends OldSocketTestCase { public void test_getTrafficClass() { try { - new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); Socket theSocket = new Socket(); /* @@ -1888,13 +1707,9 @@ public class OldSocketTest extends OldSocketTestCase { // is silently ignored String urgentData = "U"; try { - InetSocketAddress theAddress = new InetSocketAddress( - InetAddress.getLocalHost(), Support_PortManager - .getNextPort()); Socket theSocket = new Socket(); - ServerSocket serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + ServerSocket serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); Socket servSock = serverSocket.accept(); InputStream theInput = theSocket.getInputStream(); OutputStream theOutput = servSock.getOutputStream(); @@ -1931,12 +1746,9 @@ public class OldSocketTest extends OldSocketTestCase { // now validate that urgent data is received as expected. Expect // that it should be between the two writes. - theAddress = new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); servSock = serverSocket.accept(); theInput = theSocket.getInputStream(); theOutput = servSock.getOutputStream(); @@ -1973,12 +1785,9 @@ public class OldSocketTest extends OldSocketTestCase { serverSocket.close(); // now test case where we try to send two urgent bytes. - theAddress = new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); servSock = serverSocket.accept(); theInput = theSocket.getInputStream(); theOutput = servSock.getOutputStream(); @@ -2022,12 +1831,9 @@ public class OldSocketTest extends OldSocketTestCase { */ if (!platform.startsWith("Windows")) { // now test the case were we send turn the OOBInline on/off - theAddress = new InetSocketAddress(InetAddress - .getLocalHost(), Support_PortManager.getNextPort()); theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); servSock = serverSocket.accept(); theInput = theSocket.getInputStream(); theOutput = servSock.getOutputStream(); @@ -2139,12 +1945,9 @@ public class OldSocketTest extends OldSocketTestCase { } // now test the case where there is only urgent data - theAddress = new InetSocketAddress(InetAddress.getLocalHost(), - Support_PortManager.getNextPort()); theSocket = new Socket(); - serverSocket = new ServerSocket(); - serverSocket.bind(theAddress); - theSocket.connect(theAddress); + serverSocket = new ServerSocket(0, 5); + theSocket.connect(serverSocket.getLocalSocketAddress()); servSock = serverSocket.accept(); theInput = theSocket.getInputStream(); theOutput = servSock.getOutputStream(); @@ -2370,13 +2173,9 @@ public class OldSocketTest extends OldSocketTestCase { } - /** - * - */ protected int startServer(String name) { - int portNumber = Support_PortManager.getNextPort(); try { - ss = new ServerSocket(portNumber, 5); + ss = new ServerSocket(0, 5); } catch (IOException e) { fail(name + ": " + e); } 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.List; 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 @@ public class OldURLClassLoaderTest extends junit.framework.TestCase { @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 @@ public class OldURLClassLoaderTest extends junit.framework.TestCase { 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 @@ public final class URITest extends TestCase { } } + // 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..1f2ebf9 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_END; 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.SSLSocketFactory; 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; @@ -792,6 +795,27 @@ public final class URLConnectionTest extends TestCase { assertContainsNoneMatching(get.getHeaders(), "Proxy\\-Authorization.*"); } + // Don't disconnect after building a tunnel with CONNECT + // http://code.google.com/p/android/issues/detail?id=37221 + public void testProxyWithConnectionClose() throws IOException { + TestSSLContext testSSLContext = TestSSLContext.create(); + server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); + server.enqueue(new MockResponse() + .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) + .clearHeaders()); + server.enqueue(new MockResponse().setBody("this response comes via a proxy")); + server.play(); + + URL url = new URL("https://android.com/foo"); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( + server.toProxyAddress()); + connection.setRequestProperty("Connection", "close"); + connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); + connection.setHostnameVerifier(new RecordingHostnameVerifier()); + + assertContent("this response comes via a proxy", connection); + } + public void testDisconnectedConnection() throws IOException { server.enqueue(new MockResponse().setBody("ABCDEFGHIJKLMNOPQR")); server.play(); @@ -818,6 +842,50 @@ public final class URLConnectionTest extends TestCase { 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")); @@ -942,21 +1010,25 @@ public final class URLConnectionTest extends TestCase { URLConnection connection = server.getUrl("/").openConnection(); assertEquals("ABCABCABC", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); assertNull(connection.getContentEncoding()); + assertEquals(-1, connection.getContentLength()); RecordedRequest request = server.takeRequest(); assertContains(request.getHeaders(), "Accept-Encoding: gzip"); } public void testClientConfiguredGzipContentEncoding() throws Exception { + byte[] bodyBytes = gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes("UTF-8")); server.enqueue(new MockResponse() - .setBody(gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes("UTF-8"))) - .addHeader("Content-Encoding: gzip")); + .setBody(bodyBytes) + .addHeader("Content-Encoding: gzip") + .addHeader("Content-Length: " + bodyBytes.length)); server.play(); URLConnection connection = server.getUrl("/").openConnection(); connection.addRequestProperty("Accept-Encoding", "gzip"); InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream()); assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", readAscii(gunzippedIn, Integer.MAX_VALUE)); + assertEquals(bodyBytes.length, connection.getContentLength()); RecordedRequest request = server.takeRequest(); assertContains(request.getHeaders(), "Accept-Encoding: gzip"); @@ -1573,7 +1645,7 @@ public final class URLConnectionTest extends TestCase { * addresses. This is typically one IPv4 address and one IPv6 address. */ public void testConnectTimeouts() throws IOException { - StuckServer ss = new StuckServer(); + StuckServer ss = new StuckServer(false); int serverPort = ss.getLocalPort(); URLConnection urlConnection = new URL("http://localhost:" + serverPort).openConnection(); int timeout = 1000; 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 @@ public final class URLTest extends TestCase { 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 @@ public class BufferTest extends TestCase { } 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.ServerSocketChannel; 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 class OldServerSocketChannelTest extends TestCase { 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 f182375..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.UnresolvedAddressException; 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 @@ public class OldSocketChannelTest extends TestCase { 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 { @@ -287,17 +282,27 @@ public class OldSocketChannelTest extends TestCase { } public void test_socketChannel_read_DirectByteBuffer() throws InterruptedException, IOException { - - ServerThread server = new ServerThread(); + final ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.socket().bind(null, 0); + + Thread server = new Thread() { + @Override public void run() { + try { + for (int i = 0; i < 2; ++i) { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.put(data); + buf.rewind(); + ssc.accept().write(buf); + } + } catch (Exception ignored) { + } + } + }; server.start(); - Thread.currentThread().sleep(1000); - - InetSocketAddress address = new InetSocketAddress(InetAddress - .getByName("localhost"), port); // First test with array based byte buffer SocketChannel sc = SocketChannel.open(); - sc.connect(address); + sc.connect(ssc.socket().getLocalSocketAddress()); ByteBuffer buf = ByteBuffer.allocate(data.length); buf.limit(data.length / 2); @@ -313,7 +318,7 @@ public class OldSocketChannelTest extends TestCase { // Now test with direct byte buffer sc = SocketChannel.open(); - sc.connect(address); + sc.connect(ssc.socket().getLocalSocketAddress()); buf = ByteBuffer.allocateDirect(data.length); buf.limit(data.length / 2); @@ -339,32 +344,8 @@ public class OldSocketChannelTest extends TestCase { } public static boolean done = false; - public static int port = Support_PortManager.getNextPort(); public static byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - static class ServerThread extends Thread { - @Override - public void run() { - try { - ServerSocketChannel ssc = ServerSocketChannel.open(); - InetSocketAddress addr = new InetSocketAddress(InetAddress - .getByAddress(new byte[] {0, 0, 0, 0}), port); - ssc.socket().bind(addr, 0); - - ByteBuffer buf = ByteBuffer.allocate(10); - buf.put(data); - - while (!done) { - SocketChannel sc = ssc.accept(); - buf.rewind(); - sc.write(buf); - } - } catch (Exception e) { - // ignore - } - } - } - class MockSocketChannel extends SocketChannel { private boolean isConstructorCalled = false; diff --git a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java index f73b6d5..4fc70c4 100644 --- a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java +++ b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java @@ -55,11 +55,10 @@ public class SelectorTest extends TestCase { public void testNonBlockingConnect_slow() throws Exception { // Test the case where we have to wait for the connection. Selector selector = Selector.open(); - StuckServer ss = new StuckServer(); + StuckServer ss = new StuckServer(true); try { SocketChannel sc = SocketChannel.open(); sc.configureBlocking(false); - ss.unblockAfterMs(2000); sc.connect(ss.getLocalSocketAddress()); SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT); assertEquals(1, selector.select()); diff --git a/luni/src/test/java/libcore/java/security/KeyStoreTest.java b/luni/src/test/java/libcore/java/security/KeyStoreTest.java index 14d0987..ddee6ce 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.ByteArrayOutputStream; 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; @@ -44,13 +45,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import junit.framework.TestCase; -import libcore.io.IoUtils; public class KeyStoreTest extends TestCase { @@ -65,10 +66,12 @@ public class KeyStoreTest extends TestCase { private static final String ALIAS_SECRET = "secret"; private static final String ALIAS_ALT_CASE_PRIVATE = "pRiVaTe"; + private static final String ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE = "PrIvAtE-no-password"; private static final String ALIAS_ALT_CASE_CERTIFICATE = "cErTiFiCaTe"; private static final String ALIAS_ALT_CASE_SECRET = "sEcRet"; private static final String ALIAS_UNICODE_PRIVATE = "\u6400\u7902\u3101\u8c02\u5002\u8702\udd01"; + private static final String ALIAS_UNICODE_NO_PASSWORD_PRIVATE = "\u926c\u0967\uc65b\ubc78"; private static final String ALIAS_UNICODE_CERTIFICATE = "\u5402\udd01\u7902\u8702\u3101\u5f02\u3101\u5402\u5002\u8702\udd01"; private static final String ALIAS_UNICODE_SECRET = "\ue224\ud424\ud224\ue124\ud424\ue324"; @@ -146,7 +149,8 @@ public class KeyStoreTest extends TestCase { // JKS key stores cannot store secret keys, neither can the RI's PKCS12 return (!(ks.getType().equals("JKS") || ks.getType().equals("CaseExactJKS") - || (ks.getType().equals("PKCS12")))); + || (ks.getType().equals("PKCS12")) + || (ks.getType().equals("AndroidKeyStore")))); } private static boolean isCertificateEnabled(KeyStore ks) { @@ -157,13 +161,16 @@ public class KeyStoreTest extends TestCase { private static boolean isCaseSensitive(KeyStore ks) { return (ks.getType().equals("CaseExactJKS") || ks.getType().equals("BKS") - || ks.getType().equals("BouncyCastle")); + || ks.getType().equals("BouncyCastle") + || ks.getType().equals("AndroidKeyStore")); } private static boolean isUnsupported(KeyStore ks) { // Don't bother testing BC on RI - return (StandardNames.IS_RI && ks.getProvider().getName().equals("BC")); + // TODO enable AndroidKeyStore when CTS can set up the keystore + return (StandardNames.IS_RI && ks.getProvider().getName().equals("BC")) + || "AndroidKeyStore".equalsIgnoreCase(ks.getType()); } private static boolean isNullPasswordAllowed(KeyStore ks) { @@ -172,7 +179,9 @@ public class KeyStoreTest extends TestCase { || ks.getType().equals("JCEKS") || ks.getType().equals("PKCS12"))); } - + private static boolean isKeyPasswordSupported(KeyStore ks) { + return !ks.getType().equals("AndroidKeyStore"); + } private static boolean isKeyPasswordIgnored(KeyStore ks) { // BouncyCastle's PKCS12 ignores the key password unlike the RI which requires it return (ks.getType().equals("PKCS12") && ks.getProvider().getName().equals("BC")); @@ -183,6 +192,14 @@ public class KeyStoreTest extends TestCase { return (ks.getType().equals("PKCS12") && ks.getProvider().getName().equals("BC")); } + private static boolean isPersistentStorage(KeyStore ks) { + return ks.getType().equalsIgnoreCase("AndroidKeyStore"); + } + + private static boolean isLoadStoreUnsupported(KeyStore ks) { + return ks.getType().equalsIgnoreCase("AndroidKeyStore"); + } + private static boolean isSetKeyByteArrayUnimplemented(KeyStore ks) { // All of BouncyCastle's // KeyStore.setKeyEntry(String,byte[],char[]) implementations @@ -203,16 +220,13 @@ public class KeyStoreTest extends TestCase { } public static void populate(KeyStore ks) throws Exception { - ks.load(null, null); - if (isReadOnly(ks)) { - try { - setPrivateKey(ks); - fail(); - } catch (UnsupportedOperationException e) { - } + boolean readOnly = clearKeyStore(ks); + if (readOnly) { return; } - setPrivateKey(ks); + if (isKeyPasswordSupported(ks)) { + setPrivateKey(ks); + } if (isNullPasswordAllowed(ks)) { ks.setKeyEntry(ALIAS_NO_PASSWORD_PRIVATE, getPrivateKey().getPrivateKey(), @@ -234,6 +248,30 @@ public class KeyStoreTest extends TestCase { } } + private static boolean clearKeyStore(KeyStore ks) throws Exception { + ks.load(null, null); + if (isReadOnly(ks)) { + try { + setPrivateKey(ks); + fail(ks.toString()); + } catch (UnsupportedOperationException e) { + } + return true; + } + if (isPersistentStorage(ks)) { + Enumeration<String> aliases = ks.aliases(); + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + ks.deleteEntry(alias); + } + } + return false; + } + + public static void setPrivateKeyNoPassword(KeyStore ks, String alias, PrivateKeyEntry privateKey) + throws Exception { + ks.setKeyEntry(alias, privateKey.getPrivateKey(), null, privateKey.getCertificateChain()); + } public static void setPrivateKey(KeyStore ks) throws Exception { setPrivateKey(ks, ALIAS_PRIVATE); } @@ -377,7 +415,7 @@ public class KeyStoreTest extends TestCase { String type = KeyStore.getDefaultType(); try { KeyStore.getInstance(null); - fail(); + fail(type); } catch (NullPointerException expected) { } @@ -386,12 +424,12 @@ public class KeyStoreTest extends TestCase { 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 +438,7 @@ public class KeyStoreTest extends TestCase { } try { KeyStore.getInstance(type, (String)null); - fail(); + fail(type); } catch (IllegalArgumentException expected) { } assertNotNull(KeyStore.getInstance(type, providerName)); @@ -408,17 +446,17 @@ public class KeyStoreTest extends TestCase { 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 +495,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.getKey(null, null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -468,7 +506,7 @@ public class KeyStoreTest extends TestCase { // 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 +515,7 @@ public class KeyStoreTest extends TestCase { } try { keyStore.getKey(null, PASSWORD_KEY); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != NullPointerException.class && e.getClass() != IllegalArgumentException.class @@ -492,7 +530,12 @@ public class KeyStoreTest extends TestCase { if (isReadOnly(keyStore)) { assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); } else { - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + } + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); } else { @@ -503,24 +546,30 @@ public class KeyStoreTest extends TestCase { // test case insensitive if (isCaseSensitive(keyStore) || isReadOnly(keyStore)) { assertNull(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + assertNull(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, PASSWORD_KEY)); assertNull(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); } else { - assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + } + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); } } // test with null passwords - if (isKeyPasswordIgnored(keyStore)) { + if (isKeyPasswordSupported(keyStore) && isKeyPasswordIgnored(keyStore)) { assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, null)); } else { if (isReadOnly(keyStore)) { assertNull(keyStore.getKey(ALIAS_PRIVATE, null)); - } else { + } else if (isKeyPasswordSupported(keyStore)) { try { keyStore.getKey(ALIAS_PRIVATE, null); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != UnrecoverableKeyException.class && e.getClass() != IllegalArgumentException.class) { @@ -534,7 +583,7 @@ public class KeyStoreTest extends TestCase { } 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) { @@ -546,12 +595,12 @@ public class KeyStoreTest extends TestCase { // test with bad passwords if (isReadOnly(keyStore)) { assertNull(keyStore.getKey(ALIAS_PRIVATE, null)); - } else if (isKeyPasswordIgnored(keyStore)) { + } else if (isKeyPasswordSupported(keyStore) && isKeyPasswordIgnored(keyStore)) { assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, null)); - } else { + } else if (isKeyPasswordSupported(keyStore)) { try { keyStore.getKey(ALIAS_PRIVATE, PASSWORD_BAD); - fail(); + fail(keyStore.getType()); } catch (UnrecoverableKeyException expected) { } } @@ -560,7 +609,7 @@ public class KeyStoreTest extends TestCase { } else if (isSecretKeyEnabled(keyStore)) { try { keyStore.getKey(ALIAS_SECRET, PASSWORD_BAD); - fail(); + fail(keyStore.getType()); } catch (UnrecoverableKeyException expected) { } } @@ -571,7 +620,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.getCertificateChain(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -581,7 +630,7 @@ public class KeyStoreTest extends TestCase { // test odd inputs try { keyStore.getCertificateChain(null); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != NullPointerException.class && e.getClass() != IllegalArgumentException.class) { @@ -593,8 +642,10 @@ public class KeyStoreTest extends TestCase { // test case sensitive if (isReadOnly(keyStore)) { assertNull(keyStore.getCertificateChain(ALIAS_PRIVATE)); - } else { + } else if (isKeyPasswordSupported(keyStore)) { assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); + } else if (isNullPasswordAllowed(keyStore)) { + assertCertificateChain(keyStore.getCertificateChain(ALIAS_NO_PASSWORD_PRIVATE)); } // test case insensitive @@ -610,7 +661,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.getCertificate(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -620,7 +671,7 @@ public class KeyStoreTest extends TestCase { // test odd inputs try { keyStore.getCertificate(null); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != NullPointerException.class && e.getClass() != IllegalArgumentException.class) { @@ -651,20 +702,21 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.getCreationDate(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } long before = System.currentTimeMillis(); for (KeyStore keyStore : keyStores()) { + populate(keyStore); + // add 1000 since some key stores round of time to nearest second long after = System.currentTimeMillis() + 1000; - populate(keyStore); // test odd inputs try { keyStore.getCreationDate(null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } assertNull(keyStore.getCreationDate("")); @@ -673,8 +725,10 @@ public class KeyStoreTest extends TestCase { if (!isReadOnly(keyStore) && isCertificateEnabled(keyStore)) { Date date = keyStore.getCreationDate(ALIAS_CERTIFICATE); assertNotNull(date); - assertTrue(before <= date.getTime()); - assertTrue(date.getTime() <= after); + assertTrue("date should be after start time: " + date.getTime() + " >= " + before, + before <= date.getTime()); + assertTrue("date should be before expiry time: " + date.getTime() + " <= " + after, + date.getTime() <= after); } else { assertNull(keyStore.getCreationDate(ALIAS_CERTIFICATE)); } @@ -696,7 +750,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.setKeyEntry(null, null, null, null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -706,7 +760,7 @@ public class KeyStoreTest extends TestCase { if (isReadOnly(keyStore)) { try { keyStore.setKeyEntry(null, null, null, null); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; @@ -715,7 +769,7 @@ public class KeyStoreTest extends TestCase { // 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 +778,7 @@ public class KeyStoreTest extends TestCase { } 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,27 +790,43 @@ public class KeyStoreTest extends TestCase { getPrivateKey().getPrivateKey(), PASSWORD_KEY, null); - fail(); - } catch (IllegalArgumentException expected) { + fail(keyStore.getType()); + } catch (Exception e) { + if (e.getClass() != IllegalArgumentException.class + && e.getClass() != KeyStoreException.class) { + throw e; + } } } for (KeyStore keyStore : keyStores()) { - keyStore.load(null, null); + clearKeyStore(keyStore); // test case sensitive - assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + } + if (isNullPasswordAllowed(keyStore)) { + assertNull(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + } if (isReadOnly(keyStore)) { try { keyStore.setKeyEntry(ALIAS_SECRET, getSecretKey(), PASSWORD_KEY, null); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; } - setPrivateKey(keyStore); - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); + if (isKeyPasswordSupported(keyStore)) { + setPrivateKey(keyStore); + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); + } + if (isNullPasswordAllowed(keyStore)) { + setPrivateKeyNoPassword(keyStore, ALIAS_NO_PASSWORD_PRIVATE, getPrivateKey()); + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_NO_PASSWORD_PRIVATE)); + } if (isSecretKeyEnabled(keyStore)) { assertNull(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); setSecretKey(keyStore); @@ -764,7 +834,7 @@ public class KeyStoreTest extends TestCase { } 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) { @@ -783,11 +853,22 @@ public class KeyStoreTest extends TestCase { assertNull(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); assertNull(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); } else if (isCaseSensitive(keyStore)) { - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertNull(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); - setPrivateKey(keyStore, ALIAS_ALT_CASE_PRIVATE, getPrivateKey2()); - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertNull(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + setPrivateKey(keyStore, ALIAS_ALT_CASE_PRIVATE, getPrivateKey2()); + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + } + + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertNull(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + setPrivateKeyNoPassword(keyStore, ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, + getPrivateKey2()); + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); @@ -797,11 +878,22 @@ public class KeyStoreTest extends TestCase { assertSecretKey2(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); } } else { - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); - setPrivateKey(keyStore, ALIAS_ALT_CASE_PRIVATE, getPrivateKey2()); - assertPrivateKey2(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + setPrivateKey(keyStore, ALIAS_ALT_CASE_PRIVATE, getPrivateKey2()); + assertPrivateKey2(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + } + + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, null)); + assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + setPrivateKey(keyStore, ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, getPrivateKey2()); + assertPrivateKey2(keyStore.getKey(ALIAS_PRIVATE, null)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + } + if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); assertSecretKey(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); @@ -820,7 +912,7 @@ public class KeyStoreTest extends TestCase { getPrivateKey().getPrivateKey(), null, getPrivateKey().getCertificateChain()); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; @@ -839,7 +931,7 @@ public class KeyStoreTest extends TestCase { getPrivateKey().getPrivateKey(), null, getPrivateKey().getCertificateChain()); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != UnrecoverableKeyException.class && e.getClass() != IllegalArgumentException.class @@ -855,7 +947,7 @@ public class KeyStoreTest extends TestCase { } 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 +964,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.setKeyEntry(null, null, null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -883,7 +975,7 @@ public class KeyStoreTest extends TestCase { if (isReadOnly(keyStore)) { try { keyStore.setKeyEntry(null, null, null); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; @@ -892,7 +984,7 @@ public class KeyStoreTest extends TestCase { // 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 @@ -913,21 +1005,33 @@ public class KeyStoreTest extends TestCase { continue; } - keyStore.load(null, null); + clearKeyStore(keyStore); // test case sensitive - assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + } + if (isNullPasswordAllowed(keyStore)) { + assertNull(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + } if (isReadOnly(keyStore)) { try { setPrivateKeyBytes(keyStore); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; } - setPrivateKeyBytes(keyStore); - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); + if (isKeyPasswordSupported(keyStore)) { + setPrivateKeyBytes(keyStore); + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); + } + if (isNullPasswordAllowed(keyStore)) { + setPrivateKeyNoPassword(keyStore, ALIAS_NO_PASSWORD_PRIVATE, getPrivateKey()); + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_NO_PASSWORD_PRIVATE)); + } if (isSecretKeyEnabled(keyStore)) { assertNull(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); setSecretKeyBytes(keyStore); @@ -935,7 +1039,7 @@ public class KeyStoreTest extends TestCase { } else { try { keyStore.setKeyEntry(ALIAS_SECRET, getSecretKey().getEncoded(), null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -959,11 +1063,21 @@ public class KeyStoreTest extends TestCase { assertNull(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); assertNull(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); } else if (isCaseSensitive(keyStore)) { - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertNull(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); - setPrivateKeyBytes(keyStore, ALIAS_ALT_CASE_PRIVATE, getPrivateKey2()); - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertNull(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + setPrivateKeyBytes(keyStore, ALIAS_ALT_CASE_PRIVATE, getPrivateKey2()); + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + } + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertNull(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + setPrivateKeyNoPassword(keyStore, ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, + getPrivateKey2()); + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); @@ -973,11 +1087,21 @@ public class KeyStoreTest extends TestCase { assertSecretKey2(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); } } else { - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); - setPrivateKeyBytes(keyStore, ALIAS_ALT_CASE_PRIVATE, getPrivateKey2()); - assertPrivateKey2(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + setPrivateKeyBytes(keyStore, ALIAS_ALT_CASE_PRIVATE, getPrivateKey2()); + assertPrivateKey2(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + } + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertPrivateKey(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + setPrivateKeyNoPassword(keyStore, ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, + getPrivateKey2()); + assertPrivateKey2(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); @@ -994,7 +1118,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.setCertificateEntry(null, null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1005,7 +1129,7 @@ public class KeyStoreTest extends TestCase { // 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 +1141,7 @@ public class KeyStoreTest extends TestCase { try { assertNull(keyStore.getCertificate(ALIAS_CERTIFICATE)); keyStore.setCertificateEntry(ALIAS_CERTIFICATE, null); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; @@ -1031,18 +1155,22 @@ public class KeyStoreTest extends TestCase { try { int size = keyStore.size(); keyStore.setCertificateEntry(ALIAS_CERTIFICATE, null); - assertNull(keyStore.getCertificate(ALIAS_CERTIFICATE)); - assertEquals(size, keyStore.size()); - assertTrue(keyStore.isCertificateEntry(ALIAS_CERTIFICATE)); - assertTrue(Collections.list(keyStore.aliases()).contains(ALIAS_CERTIFICATE)); + assertNull(keyStore.getType(), keyStore.getCertificate(ALIAS_CERTIFICATE)); + assertEquals(keyStore.getType(), size, keyStore.size()); + assertTrue(keyStore.getType(), keyStore.isCertificateEntry(ALIAS_CERTIFICATE)); + assertTrue(keyStore.getType(), + Collections.list(keyStore.aliases()).contains(ALIAS_CERTIFICATE)); } catch (NullPointerException expectedSometimes) { - assertEquals("PKCS12", keyStore.getType()); - assertEquals("BC", keyStore.getProvider().getName()); + if (!("PKCS12".equalsIgnoreCase(keyStore.getType()) && + "BC".equalsIgnoreCase(keyStore.getProvider().getName())) + && !"AndroidKeyStore".equalsIgnoreCase(keyStore.getType())) { + throw expectedSometimes; + } } } else { try { keyStore.setCertificateEntry(ALIAS_CERTIFICATE, null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1053,14 +1181,13 @@ public class KeyStoreTest extends TestCase { continue; } - keyStore.load(null, null); + clearKeyStore(keyStore); - // test case sensitive assertNull(keyStore.getCertificate(ALIAS_CERTIFICATE)); if (isReadOnly(keyStore)) { try { setCertificate(keyStore); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; @@ -1077,10 +1204,8 @@ public class KeyStoreTest extends TestCase { populate(keyStore); if (isReadOnly(keyStore)) { - assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertNull(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); - assertNull(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); - assertNull(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); + assertNull(keyStore.getCertificate(ALIAS_CERTIFICATE)); + assertNull(keyStore.getCertificate(ALIAS_ALT_CASE_CERTIFICATE)); } else if (isCaseSensitive(keyStore)) { assertCertificate(keyStore.getCertificate(ALIAS_CERTIFICATE)); assertNull(keyStore.getCertificate(ALIAS_ALT_CASE_CERTIFICATE)); @@ -1104,7 +1229,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.deleteEntry(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1115,7 +1240,7 @@ public class KeyStoreTest extends TestCase { if (isReadOnly(keyStore)) { try { keyStore.deleteEntry(null); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; @@ -1124,7 +1249,7 @@ public class KeyStoreTest extends TestCase { // test odd inputs try { keyStore.deleteEntry(null); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != NullPointerException.class && e.getClass() != KeyStoreException.class) { @@ -1146,10 +1271,18 @@ public class KeyStoreTest extends TestCase { } // test case sensitive - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); - keyStore.deleteEntry(ALIAS_PRIVATE); - assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); + keyStore.deleteEntry(ALIAS_PRIVATE); + assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + } + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_NO_PASSWORD_PRIVATE)); + keyStore.deleteEntry(ALIAS_NO_PASSWORD_PRIVATE); + assertNull(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); @@ -1174,9 +1307,16 @@ public class KeyStoreTest extends TestCase { // test case insensitive if (isCaseSensitive(keyStore)) { - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - keyStore.deleteEntry(ALIAS_ALT_CASE_PRIVATE); - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + keyStore.deleteEntry(ALIAS_ALT_CASE_PRIVATE); + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + } + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + keyStore.deleteEntry(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE); + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); @@ -1201,17 +1341,21 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.aliases(); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } for (KeyStore keyStore : keyStores()) { keyStore.load(null, null); - if (hasDefaultContents(keyStore)) { - assertTrue(keyStore.aliases().hasMoreElements()); + if (isPersistentStorage(keyStore)) { + assertNotNull("Should be able to query size: " + keyStore.getType(), + keyStore.aliases()); + } else if (hasDefaultContents(keyStore)) { + assertTrue("Should have more than one alias already: " + keyStore.getType(), + keyStore.aliases().hasMoreElements()); } else { - assertEquals(Collections.EMPTY_SET, + assertEquals("Should have no aliases:" + keyStore.getType(), Collections.EMPTY_SET, new HashSet(Collections.list(keyStore.aliases()))); } } @@ -1220,7 +1364,9 @@ public class KeyStoreTest extends TestCase { populate(keyStore); Set<String> expected = new HashSet<String>(); - expected.add(ALIAS_PRIVATE); + if (isKeyPasswordSupported(keyStore)) { + expected.add(ALIAS_PRIVATE); + } if (isNullPasswordAllowed(keyStore)) { expected.add(ALIAS_NO_PASSWORD_PRIVATE); } @@ -1233,7 +1379,10 @@ public class KeyStoreTest extends TestCase { if (isCertificateEnabled(keyStore)) { expected.add(ALIAS_CERTIFICATE); } - if (hasDefaultContents(keyStore)) { + if (isPersistentStorage(keyStore)) { + assertNotNull("Should be able to query size: " + keyStore.getType(), + keyStore.aliases()); + } else if (hasDefaultContents(keyStore)) { assertTrue(keyStore.aliases().hasMoreElements()); } else { assertEquals(expected, new HashSet<String>(Collections.list(keyStore.aliases()))); @@ -1245,7 +1394,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.containsAlias(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1255,7 +1404,7 @@ public class KeyStoreTest extends TestCase { try { keyStore.containsAlias(null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } @@ -1271,7 +1420,11 @@ public class KeyStoreTest extends TestCase { assertFalse(keyStore.containsAlias(ALIAS_PRIVATE)); continue; } - assertTrue(keyStore.containsAlias(ALIAS_PRIVATE)); + if (isKeyPasswordSupported(keyStore)) { + assertTrue(keyStore.containsAlias(ALIAS_PRIVATE)); + } else if (isNullPasswordAllowed(keyStore)) { + assertTrue(keyStore.containsAlias(ALIAS_NO_PASSWORD_PRIVATE)); + } assertEquals(isSecretKeyEnabled(keyStore), keyStore.containsAlias(ALIAS_SECRET)); assertEquals(isCertificateEnabled(keyStore), keyStore.containsAlias(ALIAS_CERTIFICATE)); @@ -1288,28 +1441,36 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.aliases(); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } for (KeyStore keyStore : keyStores()) { keyStore.load(null, null); - if (hasDefaultContents(keyStore)) { - assertTrue(keyStore.size() > 0); + if (isPersistentStorage(keyStore)) { + assertTrue("Should successfully query size: " + keyStore.getType(), + keyStore.size() >= 0); + } else if (hasDefaultContents(keyStore)) { + assertTrue("Should have non-empty store: " + keyStore.getType(), + keyStore.size() > 0); } else { - assertEquals(0, keyStore.size()); + assertEquals("Should have empty store: " + keyStore.getType(), 0, keyStore.size()); } } for (KeyStore keyStore : keyStores()) { populate(keyStore); if (hasDefaultContents(keyStore)) { - assertTrue(keyStore.size() > 0); + assertTrue("Should have non-empty store: " + keyStore.getType(), + keyStore.size() > 0); continue; } - int expected = 1; + int expected = 0; + if (isKeyPasswordSupported(keyStore)) { + expected++; + } if (isNullPasswordAllowed(keyStore)) { expected++; } @@ -1330,7 +1491,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.isKeyEntry(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1340,7 +1501,7 @@ public class KeyStoreTest extends TestCase { try { keyStore.isKeyEntry(null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } @@ -1355,7 +1516,12 @@ public class KeyStoreTest extends TestCase { assertFalse(keyStore.isKeyEntry(ALIAS_PRIVATE)); continue; } - assertTrue(keyStore.isKeyEntry(ALIAS_PRIVATE)); + if (isKeyPasswordSupported(keyStore)) { + assertTrue(keyStore.isKeyEntry(ALIAS_PRIVATE)); + } + if (isNullPasswordAllowed(keyStore)) { + assertTrue(keyStore.isKeyEntry(ALIAS_NO_PASSWORD_PRIVATE)); + } assertEquals(isSecretKeyEnabled(keyStore), keyStore.isKeyEntry(ALIAS_SECRET)); assertFalse(keyStore.isKeyEntry(ALIAS_CERTIFICATE)); @@ -1371,7 +1537,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.isCertificateEntry(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1382,7 +1548,7 @@ public class KeyStoreTest extends TestCase { if (isCertificateEnabled(keyStore)) { try { keyStore.isCertificateEntry(null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } } else { @@ -1397,7 +1563,12 @@ public class KeyStoreTest extends TestCase { assertFalse(keyStore.isCertificateEntry("")); - assertFalse(keyStore.isCertificateEntry(ALIAS_PRIVATE)); + if (isKeyPasswordSupported(keyStore)) { + assertFalse(keyStore.isCertificateEntry(ALIAS_PRIVATE)); + } + if (isNullPasswordAllowed(keyStore)) { + assertFalse(keyStore.isCertificateEntry(ALIAS_NO_PASSWORD_PRIVATE)); + } assertFalse(keyStore.isCertificateEntry(ALIAS_SECRET)); assertEquals(isCertificateEnabled(keyStore) && !isReadOnly(keyStore), keyStore.isCertificateEntry(ALIAS_CERTIFICATE)); @@ -1415,7 +1586,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.getCertificateAlias(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1429,7 +1600,9 @@ public class KeyStoreTest extends TestCase { populate(keyStore); Set<String> expected = new HashSet<String>(); - expected.add(ALIAS_PRIVATE); + if (isKeyPasswordSupported(keyStore)) { + expected.add(ALIAS_PRIVATE); + } if (isNullPasswordAllowed(keyStore)) { expected.add(ALIAS_NO_PASSWORD_PRIVATE); } @@ -1479,7 +1652,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.store(null, null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1487,10 +1660,10 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { keyStore.load(null, null); ByteArrayOutputStream out = new ByteArrayOutputStream(); - if (isReadOnly(keyStore)) { + if (isLoadStoreUnsupported(keyStore) || isReadOnly(keyStore)) { try { keyStore.store(out, null); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; @@ -1504,7 +1677,7 @@ public class KeyStoreTest extends TestCase { try { keyStore.store(out, null); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != IllegalArgumentException.class && e.getClass() != NullPointerException.class) { @@ -1517,11 +1690,11 @@ public class KeyStoreTest extends TestCase { populate(keyStore); ByteArrayOutputStream out = new ByteArrayOutputStream(); - if (isReadOnly(keyStore)) { + if (isLoadStoreUnsupported(keyStore) || isReadOnly(keyStore)) { try { keyStore.store(out, null); - fail(); - } catch (UnsupportedOperationException e) { + fail(keyStore.getType()); + } catch (UnsupportedOperationException expected) { } } else if (isNullPasswordAllowed(keyStore)) { keyStore.store(out, null); @@ -1529,7 +1702,7 @@ public class KeyStoreTest extends TestCase { } else { try { keyStore.store(out, null); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != IllegalArgumentException.class && e.getClass() != NullPointerException.class) { @@ -1542,10 +1715,10 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { keyStore.load(null, null); ByteArrayOutputStream out = new ByteArrayOutputStream(); - if (isReadOnly(keyStore)) { + if (isLoadStoreUnsupported(keyStore) || isReadOnly(keyStore)) { try { keyStore.store(out, PASSWORD_STORE); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException e) { } continue; @@ -1557,10 +1730,10 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { populate(keyStore); ByteArrayOutputStream out = new ByteArrayOutputStream(); - if (isReadOnly(keyStore)) { + if (isLoadStoreUnsupported(keyStore) || isReadOnly(keyStore)) { try { keyStore.store(out, PASSWORD_STORE); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException e) { } continue; @@ -1574,7 +1747,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.store(null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1583,7 +1756,7 @@ public class KeyStoreTest extends TestCase { keyStore.load(null, null); try { keyStore.store(null); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { assertFalse(isLoadStoreParameterSupported(keyStore)); } catch (IllegalArgumentException expected) { @@ -1596,19 +1769,30 @@ public class KeyStoreTest extends TestCase { public void test_KeyStore_load_InputStream() throws Exception { for (KeyStore keyStore : keyStores()) { keyStore.load(null, null); - if (hasDefaultContents(keyStore)) { - assertTrue(keyStore.size() > 0); + if (isPersistentStorage(keyStore)) { + assertTrue("Should be able to query size: " + keyStore.getType(), + keyStore.size() >= 0); + } else if (hasDefaultContents(keyStore)) { + assertTrue("Should have non-empty store: " + keyStore.getType(), + keyStore.size() > 0); } else { - assertEquals(0, keyStore.size()); + assertEquals("Should have empty store: " + keyStore.getType(), 0, keyStore.size()); } } for (KeyStore keyStore : keyStores()) { + if (isLoadStoreUnsupported(keyStore)) { + continue; + } keyStore.load(null, PASSWORD_STORE); - if (hasDefaultContents(keyStore)) { - assertTrue(keyStore.size() > 0); + if (isPersistentStorage(keyStore)) { + assertTrue("Should be able to query size: " + keyStore.getType(), + keyStore.size() >= 0); + } else if (hasDefaultContents(keyStore)) { + assertTrue("Should have non-empty store: " + keyStore.getType(), + keyStore.size() > 0); } else { - assertEquals(0, keyStore.size()); + assertEquals("Should have empty store: " + keyStore.getType(), 0, keyStore.size()); } } @@ -1618,10 +1802,14 @@ public class KeyStoreTest extends TestCase { public void test_KeyStore_load_LoadStoreParameter() throws Exception { for (KeyStore keyStore : keyStores()) { keyStore.load(null); - if (hasDefaultContents(keyStore)) { - assertTrue(keyStore.size() > 0); + if (isPersistentStorage(keyStore)) { + assertTrue("Should be able to query size: " + keyStore.getType(), + keyStore.size() >= 0); + } else if (hasDefaultContents(keyStore)) { + assertTrue("Should have non-empty store: " + keyStore.getType(), + keyStore.size() > 0); } else { - assertEquals(0, keyStore.size()); + assertEquals("Should have empty store: " + keyStore.getType(), 0, keyStore.size()); } } @@ -1632,7 +1820,7 @@ public class KeyStoreTest extends TestCase { return null; } }); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } } @@ -1642,7 +1830,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.getEntry(null, null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } } @@ -1653,12 +1841,12 @@ public class KeyStoreTest extends TestCase { // 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)); @@ -1668,7 +1856,11 @@ public class KeyStoreTest extends TestCase { if (isReadOnly(keyStore)) { assertNull(keyStore.getEntry(ALIAS_PRIVATE, PARAM_KEY)); } else { - assertPrivateKey(keyStore.getEntry(ALIAS_PRIVATE, PARAM_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getEntry(ALIAS_PRIVATE, PARAM_KEY)); + } else if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getEntry(ALIAS_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getEntry(ALIAS_SECRET, PARAM_KEY)); } else { @@ -1704,12 +1896,12 @@ public class KeyStoreTest extends TestCase { assertNull(keyStore.getEntry(ALIAS_NO_PASSWORD_PRIVATE, null)); } else if (isNullPasswordAllowed(keyStore)) { assertPrivateKey(keyStore.getEntry(ALIAS_NO_PASSWORD_PRIVATE, null)); - } else if (isKeyPasswordIgnored(keyStore)) { + } else if (isKeyPasswordSupported(keyStore) && isKeyPasswordIgnored(keyStore)) { assertPrivateKey(keyStore.getEntry(ALIAS_PRIVATE, null)); - } else { + } else if (isKeyPasswordIgnored(keyStore)) { try { keyStore.getEntry(ALIAS_PRIVATE, null); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != UnrecoverableKeyException.class && e.getClass() != IllegalArgumentException.class) { @@ -1722,7 +1914,7 @@ public class KeyStoreTest extends TestCase { } 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) { @@ -1734,12 +1926,12 @@ public class KeyStoreTest extends TestCase { // test with bad passwords if (isReadOnly(keyStore)) { assertNull(keyStore.getEntry(ALIAS_PRIVATE, PARAM_BAD)); - } else if (isKeyPasswordIgnored(keyStore)) { + } else if (isKeyPasswordSupported(keyStore) && isKeyPasswordIgnored(keyStore)) { assertPrivateKey(keyStore.getEntry(ALIAS_PRIVATE, PARAM_BAD)); - } else { + } else if (isKeyPasswordSupported(keyStore)) { try { keyStore.getEntry(ALIAS_PRIVATE, PARAM_BAD); - fail(); + fail(keyStore.getType()); } catch (UnrecoverableKeyException expected) { } } @@ -1748,19 +1940,22 @@ public class KeyStoreTest extends TestCase { } 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 +1963,20 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { keyStore.load(null, null); + try { + keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), new FakeProtectionParameter()); + fail("Should not accept unknown ProtectionParameter: " + keyStore.getProvider()); + } 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 +1985,7 @@ public class KeyStoreTest extends TestCase { } try { keyStore.setEntry(null, null, PARAM_KEY); - fail(); + fail(keyStore.getType()); } catch (Exception e) { if (e.getClass() != NullPointerException.class && e.getClass() != KeyStoreException.class) { @@ -1789,27 +1994,34 @@ public class KeyStoreTest extends TestCase { } try { keyStore.setEntry("", null, PARAM_KEY); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } } for (KeyStore keyStore : keyStores()) { - keyStore.load(null, null); + clearKeyStore(keyStore); // test case sensitive assertNull(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); if (isReadOnly(keyStore)) { try { keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), PARAM_KEY); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; } - keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), PARAM_KEY); - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); + if (isKeyPasswordSupported(keyStore)) { + keyStore.setEntry(ALIAS_PRIVATE, getPrivateKey(), PARAM_KEY); + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_PRIVATE)); + } + if (isNullPasswordAllowed(keyStore)) { + keyStore.setEntry(ALIAS_NO_PASSWORD_PRIVATE, getPrivateKey(), null); + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_NO_PASSWORD_PRIVATE)); + } if (isSecretKeyEnabled(keyStore)) { assertNull(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); keyStore.setEntry(ALIAS_SECRET, new SecretKeyEntry(getSecretKey()), PARAM_KEY); @@ -1817,7 +2029,7 @@ public class KeyStoreTest extends TestCase { } else { try { keyStore.setKeyEntry(ALIAS_SECRET, getSecretKey(), PASSWORD_KEY, null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1832,13 +2044,21 @@ public class KeyStoreTest extends TestCase { keyStore.setEntry(ALIAS_CERTIFICATE, new TrustedCertificateEntry(getPrivateKey().getCertificate()), null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } - keyStore.setEntry(ALIAS_UNICODE_PRIVATE, getPrivateKey(), PARAM_KEY); - assertPrivateKey(keyStore.getKey(ALIAS_UNICODE_PRIVATE, PASSWORD_KEY)); - assertCertificateChain(keyStore.getCertificateChain(ALIAS_UNICODE_PRIVATE)); + if (isKeyPasswordSupported(keyStore)) { + keyStore.setEntry(ALIAS_UNICODE_PRIVATE, getPrivateKey(), PARAM_KEY); + assertPrivateKey(keyStore.getKey(ALIAS_UNICODE_PRIVATE, PASSWORD_KEY)); + assertCertificateChain(keyStore.getCertificateChain(ALIAS_UNICODE_PRIVATE)); + } + if (isNullPasswordAllowed(keyStore)) { + keyStore.setEntry(ALIAS_UNICODE_NO_PASSWORD_PRIVATE, getPrivateKey(), null); + assertPrivateKey(keyStore.getKey(ALIAS_UNICODE_NO_PASSWORD_PRIVATE, null)); + assertCertificateChain(keyStore + .getCertificateChain(ALIAS_UNICODE_NO_PASSWORD_PRIVATE)); + } if (isSecretKeyEnabled(keyStore)) { assertNull(keyStore.getKey(ALIAS_UNICODE_SECRET, PASSWORD_KEY)); keyStore.setEntry(ALIAS_UNICODE_SECRET, new SecretKeyEntry(getSecretKey()), PARAM_KEY); @@ -1846,7 +2066,7 @@ public class KeyStoreTest extends TestCase { } else { try { keyStore.setKeyEntry(ALIAS_UNICODE_SECRET, getSecretKey(), PASSWORD_KEY, null); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1861,11 +2081,21 @@ public class KeyStoreTest extends TestCase { assertNull(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); assertNull(keyStore.getKey(ALIAS_ALT_CASE_SECRET, PASSWORD_KEY)); } else if (isCaseSensitive(keyStore)) { - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertNull(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); - keyStore.setEntry(ALIAS_ALT_CASE_PRIVATE, getPrivateKey2(), PARAM_KEY); - assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); - assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + if (isKeyPasswordSupported(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertNull(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + keyStore.setEntry(ALIAS_ALT_CASE_PRIVATE, getPrivateKey2(), PARAM_KEY); + assertPrivateKey(keyStore.getKey(ALIAS_PRIVATE, PASSWORD_KEY)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_PRIVATE, PASSWORD_KEY)); + } + + if (isNullPasswordAllowed(keyStore)) { + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertNull(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + keyStore.setEntry(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, getPrivateKey2(), null); + assertPrivateKey(keyStore.getKey(ALIAS_NO_PASSWORD_PRIVATE, null)); + assertPrivateKey2(keyStore.getKey(ALIAS_ALT_CASE_NO_PASSWORD_PRIVATE, null)); + } if (isSecretKeyEnabled(keyStore)) { assertSecretKey(keyStore.getKey(ALIAS_SECRET, PASSWORD_KEY)); @@ -1931,20 +2161,33 @@ public class KeyStoreTest extends TestCase { 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 +2196,22 @@ public class KeyStoreTest extends TestCase { } } } - 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 +2225,7 @@ public class KeyStoreTest extends TestCase { new TrustedCertificateEntry( getPrivateKey().getCertificate()), PARAM_KEY); - fail(); + fail(keyStore.getType()); } catch (KeyStoreException expected) { } } @@ -1987,7 +2237,7 @@ public class KeyStoreTest extends TestCase { for (KeyStore keyStore : keyStores()) { try { keyStore.entryInstanceOf(null, null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } } @@ -1997,17 +2247,17 @@ public class KeyStoreTest extends TestCase { 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) { } @@ -2040,10 +2290,17 @@ public class KeyStoreTest extends TestCase { } // test case sensitive - assertTrue(keyStore.entryInstanceOf(ALIAS_PRIVATE, PrivateKeyEntry.class)); + assertEquals(isKeyPasswordSupported(keyStore), + keyStore.entryInstanceOf(ALIAS_PRIVATE, PrivateKeyEntry.class)); assertFalse(keyStore.entryInstanceOf(ALIAS_PRIVATE, SecretKeyEntry.class)); assertFalse(keyStore.entryInstanceOf(ALIAS_PRIVATE, TrustedCertificateEntry.class)); + assertEquals(isNullPasswordAllowed(keyStore), + keyStore.entryInstanceOf(ALIAS_NO_PASSWORD_PRIVATE, PrivateKeyEntry.class)); + assertFalse(keyStore.entryInstanceOf(ALIAS_NO_PASSWORD_PRIVATE, SecretKeyEntry.class)); + assertFalse(keyStore.entryInstanceOf(ALIAS_NO_PASSWORD_PRIVATE, + TrustedCertificateEntry.class)); + assertEquals(isSecretKeyEnabled(keyStore), keyStore.entryInstanceOf(ALIAS_SECRET, SecretKeyEntry.class)); assertFalse(keyStore.entryInstanceOf(ALIAS_SECRET, PrivateKeyEntry.class)); @@ -2082,7 +2339,7 @@ public class KeyStoreTest extends TestCase { keyStore.load(null, null); try { Builder.newInstance(keyStore, null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } } @@ -2092,7 +2349,7 @@ public class KeyStoreTest extends TestCase { Builder.newInstance(keyStore.getType(), keyStore.getProvider(), null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } } @@ -2103,7 +2360,7 @@ public class KeyStoreTest extends TestCase { null, null, null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } try { @@ -2111,7 +2368,7 @@ public class KeyStoreTest extends TestCase { keyStore.getProvider(), null, null); - fail(); + fail(keyStore.getType()); } catch (NullPointerException expected) { } } @@ -2121,13 +2378,13 @@ public class KeyStoreTest extends TestCase { 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("")); @@ -2140,10 +2397,10 @@ public class KeyStoreTest extends TestCase { OutputStream os = null; try { os = new FileOutputStream(file); - if (isReadOnly(keyStore)) { + if (isLoadStoreUnsupported(keyStore) || isReadOnly(keyStore)) { try { keyStore.store(os, PASSWORD_STORE); - fail(); + fail(keyStore.getType()); } catch (UnsupportedOperationException expected) { } continue; @@ -2160,12 +2417,20 @@ public class KeyStoreTest extends TestCase { 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(); } } for (KeyStore keyStore : keyStores()) { + if (isLoadStoreUnsupported(keyStore)) { + continue; + } Builder builder = Builder.newInstance(keyStore.getType(), keyStore.getProvider(), PARAM_STORE); @@ -2215,7 +2480,7 @@ public class KeyStoreTest extends TestCase { // 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 +2490,7 @@ public class KeyStoreTest extends TestCase { } 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 @@ public class ProviderTest extends TestCase { } } } + + /** + * 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..63daa88 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.Provider; 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 @@ public class SignatureTest extends TestCase { 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 @@ public class SignatureTest extends TestCase { (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 @@ public class SignatureTest extends TestCase { (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 @@ public class SignatureTest extends TestCase { 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 @@ public class SignatureTest extends TestCase { try { sig.initSign(privKey); fail("Should throw error when modulus is not available"); - } catch (InvalidKeyException e) { - // success + } catch (InvalidKeyException expected) { } } @@ -781,8 +843,7 @@ public class SignatureTest extends TestCase { try { sig.initSign(privKey); fail("Should throw error when key is empty"); - } catch (InvalidKeyException e) { - // success + } catch (InvalidKeyException expected) { } } @@ -902,6 +963,165 @@ public class SignatureTest extends TestCase { 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: * @@ -1033,4 +1253,23 @@ public class SignatureTest extends TestCase { sig.update(Vector2Data); assertTrue("Signature must verify correctly", sig.verify(SHA1withDSA_Vector2Signature)); } + + // NetscapeCertRequest looks up Signature algorithms by OID from + // BC but BC version 1.47 had registration bugs and MD5withRSA was + // overlooked. http://b/7453821 + public void testGetInstanceFromOID() throws Exception { + assertBouncyCastleSignatureFromOID("1.2.840.113549.1.1.4"); // MD5withRSA + assertBouncyCastleSignatureFromOID("1.2.840.113549.1.1.5"); // SHA1withRSA + assertBouncyCastleSignatureFromOID("1.3.14.3.2.29"); // SHA1withRSA + assertBouncyCastleSignatureFromOID("1.2.840.113549.1.1.11"); // SHA256withRSA + assertBouncyCastleSignatureFromOID("1.2.840.113549.1.1.12"); // SHA384withRSA + assertBouncyCastleSignatureFromOID("1.2.840.113549.1.1.13"); // SHA512withRSA + assertBouncyCastleSignatureFromOID("1.2.840.10040.4.3"); // SHA1withDSA + } + + private void assertBouncyCastleSignatureFromOID(String oid) throws Exception { + Signature signature = Signature.getInstance(oid, "BC"); + assertNotNull(oid, signature); + assertEquals(oid, signature.getAlgorithm()); + } } diff --git a/luni/src/test/java/libcore/java/text/OldDateFormatTest.java b/luni/src/test/java/libcore/java/text/OldDateFormatTest.java index f614d6f..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 @@ public class OldDateFormatTest extends junit.framework.TestCase { 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 @@ public class OldDateFormatTest extends junit.framework.TestCase { 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 @@ -203,7 +203,7 @@ public class OldDateFormatTest extends junit.framework.TestCase { /** * java.text.DateFormat#parse(String) */ - public void test_parseLString() { + public void test_parseLString() throws Exception { DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.US); try { @@ -338,35 +338,29 @@ public class OldDateFormatTest extends junit.framework.TestCase { try { format.parse("January 31 1970 7:52:34 AM PST"); fail("ParseException was not thrown."); - } catch(ParseException pe) { - //expected + } catch (ParseException expected) { } try { format.parse("January 31 1970"); fail("ParseException was not thrown."); - } catch(ParseException pe) { - //expected + } catch (ParseException expected) { } format = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.US); - try { - Date date = format.parse(format.format(current).toString()); - assertEquals(current.getDate(), date.getDate()); - assertEquals(current.getDay(), date.getDay()); - assertEquals(current.getMonth(), date.getMonth()); - assertEquals(current.getYear(), date.getYear()); - assertEquals(current.getHours(), date.getHours()); - assertEquals(current.getMinutes(), date.getMinutes()); - } catch(ParseException pe) { - fail("ParseException was thrown for current Date."); + String formatPattern = ((SimpleDateFormat) format).toPattern(); + String formattedCurrent = format.format(current); + Date date = format.parse(formattedCurrent); + // Date has millisecond accuracy, but humans don't use time formats that precise. + if (date.getTime() / 1000 != current.getTime() / 1000) { + fail(date.getTime() + " != " + current.getTime() + + "; " + formatPattern + "; " + formattedCurrent); } try { format.parse("January 16, 1970 8:03:52 PM CET"); fail("ParseException was not thrown."); - } catch(ParseException pe) { - //expected + } catch (ParseException expected) { } } diff --git a/luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java b/luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java index 01506df..ff24bb6 100644 --- a/luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java +++ b/luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java @@ -30,9 +30,21 @@ import java.util.TimeZone; public class OldSimpleDateFormatTest extends junit.framework.TestCase { - SimpleDateFormat format = new SimpleDateFormat("", Locale.ENGLISH); + SimpleDateFormat format = null; + SimpleDateFormat pFormat = null; + + @Override + protected void setUp() throws Exception { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + format = new SimpleDateFormat("", Locale.ENGLISH); + pFormat = new SimpleDateFormat("", Locale.ENGLISH); + } - SimpleDateFormat pFormat = new SimpleDateFormat("", Locale.ENGLISH); + @Override + protected void tearDown() throws Exception { + format = null; + pFormat = null; + } class FormatTester { boolean testsFailed = false; 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 @@ public class SimpleDateFormatTest extends junit.framework.TestCase { // 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 class SimpleDateFormatTest extends junit.framework.TestCase { } 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 @@ public class SimpleDateFormatTest extends junit.framework.TestCase { * 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 class SimpleDateFormatTest extends junit.framework.TestCase { } 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 @@ public class SimpleDateFormatTest extends junit.framework.TestCase { // 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 class SimpleDateFormatTest extends junit.framework.TestCase { } 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 @@ public class SimpleDateFormatTest extends junit.framework.TestCase { 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/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java index 541dd66..b146b1a 100644 --- a/luni/src/test/java/libcore/java/util/LocaleTest.java +++ b/luni/src/test/java/libcore/java/util/LocaleTest.java @@ -46,6 +46,16 @@ public class LocaleTest extends junit.framework.TestCase { assertEquals("Deutsch", Locale.GERMAN.getDisplayLanguage(Locale.GERMAN)); } + // http://b/7291355; Locale.getDisplayLanguage fails for tl in tl in ICU 4.9. + public void test_tl() throws Exception { + Locale tl = new Locale("tl"); + Locale tl_PH = new Locale("tl", "PH"); + assertEquals("Filipino", tl.getDisplayLanguage(Locale.ENGLISH)); + assertEquals("Filipino", tl_PH.getDisplayLanguage(Locale.ENGLISH)); + assertEquals("Filipino", tl.getDisplayLanguage(tl)); + assertEquals("Filipino", tl_PH.getDisplayLanguage(tl_PH)); + } + // http://b/3452611; Locale.getDisplayLanguage fails for the obsolete language codes. public void test_getDisplayName_obsolete() throws Exception { // he (new) -> iw (obsolete) 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 @@ import junit.framework.TestCase; * 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 @@ public class OldAndroidLocaleTest extends TestCase { 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 @@ public class OldAndroidLocaleTest extends TestCase { 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.Scanner; 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 @@ public final class OldScannerTest extends TestCase { 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 class OldTimeZoneTest extends TestCase { } 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 class OldTimeZoneTest extends TestCase { } 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 @@ public class OldTimeZoneTest extends TestCase { 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 @@ public class TimeZoneTest extends junit.framework.TestCase { // 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 @@ public class TimeZoneTest extends junit.framework.TestCase { } 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/prefs/OldPreferencesTest.java b/luni/src/test/java/libcore/java/util/prefs/OldPreferencesTest.java index aa5f088..f8a8154 100644 --- a/luni/src/test/java/libcore/java/util/prefs/OldPreferencesTest.java +++ b/luni/src/test/java/libcore/java/util/prefs/OldPreferencesTest.java @@ -29,8 +29,10 @@ import junit.framework.TestCase; public final class OldPreferencesTest extends TestCase { - final static String longKey; - final static String longValue; + private static final boolean ENCOURAGE_RACES = false; + + private static final String longKey; + private static final String longValue; static { StringBuilder key = new StringBuilder(Preferences.MAX_KEY_LENGTH); @@ -316,8 +318,7 @@ public final class OldPreferencesTest extends TestCase { String longName = name.toString(); Preferences root = Preferences.userRoot(); - Preferences parent = Preferences - .userNodeForPackage(Preferences.class); + Preferences parent = Preferences.userNodeForPackage(Preferences.class); Preferences pref = parent.node("mock"); try { @@ -352,8 +353,7 @@ public final class OldPreferencesTest extends TestCase { public void testNodeExists() throws BackingStoreException { - Preferences parent = Preferences - .userNodeForPackage(Preferences.class); + Preferences parent = Preferences.userNodeForPackage(Preferences.class); Preferences pref = parent.node("mock"); try { @@ -393,16 +393,14 @@ public final class OldPreferencesTest extends TestCase { } public void testParent() { - Preferences parent = Preferences - .userNodeForPackage(Preferences.class); + Preferences parent = Preferences.userNodeForPackage(Preferences.class); Preferences pref = parent.node("mock"); assertSame(parent, pref.parent()); } public void testPut() throws BackingStoreException { - Preferences pref = Preferences - .userNodeForPackage(Preferences.class); + Preferences pref = Preferences.userNodeForPackage(Preferences.class); pref.put("", "emptyvalue"); assertEquals("emptyvalue", pref.get("", null)); pref.put("testPutkey", "value1"); @@ -468,8 +466,7 @@ public final class OldPreferencesTest extends TestCase { } public void testPutDouble() { - Preferences pref = Preferences - .userNodeForPackage(Preferences.class); + Preferences pref = Preferences.userNodeForPackage(Preferences.class); try { pref.putDouble(null, 3); fail(); @@ -580,8 +577,7 @@ public final class OldPreferencesTest extends TestCase { } public void testRemove() throws BackingStoreException { - Preferences pref = Preferences - .userNodeForPackage(Preferences.class); + Preferences pref = Preferences.userNodeForPackage(Preferences.class); pref.remove("key"); pref.put("key", "value"); @@ -606,8 +602,7 @@ public final class OldPreferencesTest extends TestCase { } public void testRemoveNode() throws BackingStoreException { - Preferences pref = Preferences - .userNodeForPackage(Preferences.class); + Preferences pref = Preferences.userNodeForPackage(Preferences.class); Preferences child = pref.node("child"); Preferences child1 = pref.node("child1"); Preferences grandchild = child.node("grandchild"); @@ -636,11 +631,12 @@ public final class OldPreferencesTest extends TestCase { nl = new MockNodeChangeListener(); pref.addNodeChangeListener(nl); child1 = pref.node("mock1"); - nl.waitForEvent(); + nl.waitForEvent(1); assertEquals(1, nl.getAdded()); nl.reset(); pref.node("mock1"); - nl.waitForEvent(); + // There shouldn't be an event, but wait in case one arrives... + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} assertEquals(0, nl.getAdded()); nl.reset(); } finally { @@ -653,7 +649,7 @@ public final class OldPreferencesTest extends TestCase { pref.addNodeChangeListener(nl); pref.addNodeChangeListener(nl); child1 = pref.node("mock2"); - nl.waitForEvent(); + nl.waitForEvent(2); assertEquals(2, nl.getAdded()); nl.reset(); } finally { @@ -667,7 +663,7 @@ public final class OldPreferencesTest extends TestCase { pref.addNodeChangeListener(nl); child1 = pref.node("mock3"); child1.removeNode(); - nl.waitForEvent(); + nl.waitForEvent(2); assertEquals(1, nl.getRemoved()); nl.reset(); } finally { @@ -680,7 +676,7 @@ public final class OldPreferencesTest extends TestCase { pref.addNodeChangeListener(nl); child1 = pref.node("mock6"); child1.removeNode(); - nl.waitForEvent(); + nl.waitForEvent(4); assertEquals(2, nl.getRemoved()); nl.reset(); } finally { @@ -695,27 +691,29 @@ public final class OldPreferencesTest extends TestCase { child1 = pref.node("mock4"); child1.addNodeChangeListener(nl); pref.node("mock4/mock5"); - nl.waitForEvent(); + nl.waitForEvent(1); assertEquals(1, nl.getAdded()); nl.reset(); child3 = pref.node("mock4/mock5/mock6"); - nl.waitForEvent(); + // There shouldn't be an event, but wait in case one arrives... + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} assertEquals(0, nl.getAdded()); nl.reset(); child3.removeNode(); - nl.waitForEvent(); + // There shouldn't be an event, but wait in case one arrives... + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} assertEquals(0, nl.getRemoved()); nl.reset(); pref.node("mock4/mock7"); - nl.waitForEvent(); + nl.waitForEvent(1); assertEquals(1, nl.getAdded()); nl.reset(); child1.removeNode(); - nl.waitForEvent(); - assertEquals(2, nl.getRemoved()); // fail 1 + nl.waitForEvent(2); + assertEquals(2, nl.getRemoved()); nl.reset(); } finally { try { @@ -749,7 +747,7 @@ public final class OldPreferencesTest extends TestCase { try { pref.clear(); pl.waitForEvent(2); - assertEquals(2, pl.getChanged()); // fail 1 + assertEquals(2, pl.getChanged()); } catch(BackingStoreException bse) { pl.reset(); fail("BackingStoreException is thrown"); @@ -784,7 +782,7 @@ public final class OldPreferencesTest extends TestCase { try { pref.clear(); pl.waitForEvent(3); - assertEquals(3, pl.getChanged()); // fails + assertEquals(3, pl.getChanged()); } catch(BackingStoreException bse) { fail("BackingStoreException is thrown"); } @@ -868,25 +866,38 @@ public final class OldPreferencesTest extends TestCase { static class MockNodeChangeListener implements NodeChangeListener { private int added = 0; private int removed = 0; + private int eventCount = 0; - public synchronized void waitForEvent() { - try { - wait(500); - } catch (InterruptedException ignored) { + public void waitForEvent(int count) { + synchronized (this) { + while (eventCount < count) { + try { + wait(); + } catch (InterruptedException ignored) { + } + } } } - public synchronized void childAdded(NodeChangeEvent e) { - ++added; - notifyAll(); + public void childAdded(NodeChangeEvent e) { + if (ENCOURAGE_RACES) try { Thread.sleep(1000); } catch (InterruptedException ignored) { } + synchronized (this) { + ++eventCount; + ++added; + notifyAll(); + } } - public synchronized void childRemoved(NodeChangeEvent e) { - removed++; - notifyAll(); + public void childRemoved(NodeChangeEvent e) { + if (ENCOURAGE_RACES) try { Thread.sleep(1000); } catch (InterruptedException ignored) { } + synchronized (this) { + ++eventCount; + ++removed; + notifyAll(); + } } - public synchronized int getAdded() { + public synchronized int getAdded() { return added; } @@ -894,7 +905,8 @@ public final class OldPreferencesTest extends TestCase { return removed; } - public void reset() { + public synchronized void reset() { + eventCount = 0; added = 0; removed = 0; } @@ -902,47 +914,35 @@ public final class OldPreferencesTest extends TestCase { private static class MockPreferenceChangeListener implements PreferenceChangeListener { private int changed = 0; - private boolean addDispatched = false; - private boolean result = false; + private int eventCount = 0; public void waitForEvent(int count) { - for (int i = 0; i < count; i++) { - try { - synchronized (this) { - wait(500); + synchronized (this) { + while (eventCount < count) { + try { + wait(); + } catch (InterruptedException ignored) { } - } catch (InterruptedException ignored) { } } } - public synchronized void preferenceChange(PreferenceChangeEvent pce) { - changed++; - addDispatched = true; - notifyAll(); - } - - public boolean getResult() throws InterruptedException { - if (!addDispatched) { - // TODO: don't know why must add limitation - wait(100); + public void preferenceChange(PreferenceChangeEvent pce) { + if (ENCOURAGE_RACES) try { Thread.sleep(1000); } catch (InterruptedException ignored) { } + synchronized (this) { + ++eventCount; + ++changed; + notifyAll(); } - addDispatched = false; - return result; } - public synchronized int getChanged() throws InterruptedException { - if (!addDispatched) { - // TODO: don't know why must add limitation - wait(1000); - } - addDispatched = false; + public synchronized int getChanged() { return changed; } - public void reset() { + public synchronized void reset() { + eventCount = 0; changed = 0; - result = false; } } 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 @@ public class OldMatcherTest extends TestCase { } } - // 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); + + // 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(-1)); 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 @@ public class OldMatcherTest extends TestCase { 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..68a46f3 100644 --- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java +++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java @@ -17,14 +17,788 @@ package libcore.javax.crypto; import com.android.org.bouncycastle.asn1.x509.KeyUsage; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; import java.security.cert.Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import javax.crypto.BadPaddingException; import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.SecretKeySpec; import junit.framework.TestCase; +import libcore.java.security.StandardNames; import libcore.java.security.TestKeyStore; public final class CipherTest extends TestCase { + private static final String[] RSA_PROVIDERS = ((StandardNames.IS_RI) + ? new String[] { "SunJCE" } + : new String[] { "BC" , "AndroidOpenSSL" }); + + private static final String[] AES_PROVIDERS = ((StandardNames.IS_RI) + ? new String[] { "SunJCE" } + : new String[] { "BC" }); // TOOD: , "AndroidOpenSSL" + + private static final boolean IS_UNLIMITED; + static { + boolean is_unlimited; + if (StandardNames.IS_RI) { + try { + String algorithm = "PBEWITHMD5ANDTRIPLEDES"; + Cipher.getInstance(algorithm).init(getEncryptMode(algorithm), + getEncryptKey(algorithm), + getAlgorithmParameterSpec(algorithm)); + is_unlimited = true; + } catch (Exception e) { + is_unlimited = false; + System.out.println("WARNING: Some tests disabled due to lack of " + + "'Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files'"); + } + } else { + is_unlimited = true; + } + IS_UNLIMITED = is_unlimited; + } + + private static boolean isUnsupported(String algorithm) { + if (algorithm.equals("RC2")) { + return true; + } + if (algorithm.equals("PBEWITHMD5ANDRC2")) { + return true; + } + if (algorithm.startsWith("PBEWITHSHA1ANDRC2")) { + return true; + } + if (algorithm.equals("PBEWITHSHAAND40BITRC2-CBC")) { + return true; + } + if (algorithm.equals("PBEWITHSHAAND128BITRC2-CBC")) { + return true; + } + if (algorithm.equals("PBEWITHSHAANDTWOFISH-CBC")) { + return true; + } + if (!IS_UNLIMITED) { + if (algorithm.equals("PBEWITHMD5ANDTRIPLEDES")) { + 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 getBaseAlgorithm(String algorithm) { + if (algorithm.equals("AESWRAP")) { + return "AES"; + } + if (algorithm.startsWith("AES/")) { + 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("PBEWITHMD5ANDTRIPLEDES")) { + return "DESEDE"; + } + if (algorithm.equals("PBEWITHSHA1ANDDESEDE")) { + 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 getBaseAlgorithm(algorithm).equals("RSA"); + } + + private static boolean isWrap(String algorithm) { + return algorithm.endsWith("WRAP"); + } + + private static boolean isPBE(String algorithm) { + return algorithm.startsWith("PBE"); + } + + 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; + } + if (algorithm.startsWith("RSA")) { + KeyFactory kf = KeyFactory.getInstance("RSA"); + RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus, + RSA_2048_privateExponent); + key = kf.generatePrivate(keySpec); + } else if (isPBE(algorithm)) { + SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm); + key = skf.generateSecret(new PBEKeySpec("secret".toCharArray())); + } else { + KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(algorithm)); + if (StandardNames.IS_RI && algorithm.equals("AES")) { + kg.init(128); + } + 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; + } + if (algorithm.startsWith("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 { + setExpectedBlockSize("AES", 16); + setExpectedBlockSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16); + setExpectedBlockSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16); + setExpectedBlockSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16); + setExpectedBlockSize("PBEWITHSHA256AND128BITAES-CBC-BC", 16); + setExpectedBlockSize("PBEWITHSHA256AND192BITAES-CBC-BC", 16); + setExpectedBlockSize("PBEWITHSHA256AND256BITAES-CBC-BC", 16); + setExpectedBlockSize("PBEWITHSHAAND128BITAES-CBC-BC", 16); + setExpectedBlockSize("PBEWITHSHAAND192BITAES-CBC-BC", 16); + setExpectedBlockSize("PBEWITHSHAAND256BITAES-CBC-BC", 16); + + if (StandardNames.IS_RI) { + setExpectedBlockSize("AESWRAP", 16); + } else { + setExpectedBlockSize("AESWRAP", 0); + } + + setExpectedBlockSize("ARC4", 0); + setExpectedBlockSize("ARCFOUR", 0); + setExpectedBlockSize("PBEWITHSHAAND40BITRC4", 0); + setExpectedBlockSize("PBEWITHSHAAND128BITRC4", 0); + + setExpectedBlockSize("BLOWFISH", 8); + + setExpectedBlockSize("DES", 8); + setExpectedBlockSize("PBEWITHMD5ANDDES", 8); + setExpectedBlockSize("PBEWITHSHA1ANDDES", 8); + + setExpectedBlockSize("DESEDE", 8); + setExpectedBlockSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", 8); + setExpectedBlockSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", 8); + setExpectedBlockSize("PBEWITHMD5ANDTRIPLEDES", 8); + setExpectedBlockSize("PBEWITHSHA1ANDDESEDE", 8); + + + if (StandardNames.IS_RI) { + setExpectedBlockSize("DESEDEWRAP", 8); + } else { + setExpectedBlockSize("DESEDEWRAP", 0); + } + + if (StandardNames.IS_RI) { + setExpectedBlockSize("RSA", 0); + setExpectedBlockSize("RSA/ECB/NoPadding", 0); + setExpectedBlockSize("RSA/ECB/PKCS1Padding", 0); + } else { + setExpectedBlockSize("RSA", Cipher.ENCRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/NoPadding", Cipher.ENCRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/PKCS1Padding", Cipher.ENCRYPT_MODE, 245); + + // BC strips the leading 0 for us even when NoPadding is specified + setExpectedBlockSize("RSA", Cipher.ENCRYPT_MODE, "BC", 255); + setExpectedBlockSize("RSA/ECB/NoPadding", Cipher.ENCRYPT_MODE, "BC", 255); + + setExpectedBlockSize("RSA", Cipher.DECRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, 256); + setExpectedBlockSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, 256); + } + } + + private static String modeKey(String algorithm, int mode) { + return algorithm + ":" + mode; + } + + private static String modeProviderKey(String algorithm, int mode, String provider) { + return algorithm + ":" + mode + ":" + provider; + } + + private static void setExpectedSize(Map<String, Integer> map, + String algorithm, int value) { + algorithm = algorithm.toUpperCase(Locale.US); + map.put(algorithm, value); + } + + private static void setExpectedSize(Map<String, Integer> map, + String algorithm, int mode, int value) { + setExpectedSize(map, modeKey(algorithm, mode), value); + } + + private static void setExpectedSize(Map<String, Integer> map, + String algorithm, int mode, String provider, int value) { + setExpectedSize(map, modeProviderKey(algorithm, mode, provider), value); + } + + private static int getExpectedSize(Map<String, Integer> map, String algorithm, int mode, String provider) { + Integer expected = map.get(modeProviderKey(algorithm, mode, provider)); + if (expected != null) { + return expected; + } + expected = map.get(modeKey(algorithm, mode)); + if (expected != null) { + return expected; + } + expected = map.get(algorithm); + assertNotNull("Algorithm " + algorithm + " not found in " + map, expected); + return expected; + } + + private static void setExpectedBlockSize(String algorithm, int value) { + setExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, value); + } + + private static void setExpectedBlockSize(String algorithm, int mode, int value) { + setExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, mode, value); + } + + private static void setExpectedBlockSize(String algorithm, int mode, String provider, int value) { + setExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, mode, provider, value); + } + + private static int getExpectedBlockSize(String algorithm, int mode, String provider) { + return getExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, mode, provider); + } + + private static Map<String, Integer> EXPECTED_OUTPUT_SIZE = new HashMap<String, Integer>(); + static { + setExpectedOutputSize("AES", Cipher.ENCRYPT_MODE, 16); + setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16); + setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16); + setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16); + setExpectedOutputSize("PBEWITHSHA256AND128BITAES-CBC-BC", 16); + setExpectedOutputSize("PBEWITHSHA256AND192BITAES-CBC-BC", 16); + setExpectedOutputSize("PBEWITHSHA256AND256BITAES-CBC-BC", 16); + setExpectedOutputSize("PBEWITHSHAAND128BITAES-CBC-BC", 16); + setExpectedOutputSize("PBEWITHSHAAND192BITAES-CBC-BC", 16); + setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", 16); + + setExpectedOutputSize("AES", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHA256AND128BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHA256AND192BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHA256AND256BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHAAND128BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHAAND192BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0); + + if (StandardNames.IS_RI) { + setExpectedOutputSize("AESWRAP", Cipher.WRAP_MODE, 8); + setExpectedOutputSize("AESWRAP", Cipher.UNWRAP_MODE, 0); + } else { + setExpectedOutputSize("AESWRAP", -1); + } + + setExpectedOutputSize("ARC4", 0); + setExpectedOutputSize("ARCFOUR", 0); + setExpectedOutputSize("PBEWITHSHAAND40BITRC4", 0); + setExpectedOutputSize("PBEWITHSHAAND128BITRC4", 0); + + setExpectedOutputSize("BLOWFISH", Cipher.ENCRYPT_MODE, 8); + setExpectedOutputSize("BLOWFISH", Cipher.DECRYPT_MODE, 0); + + setExpectedOutputSize("DES", Cipher.ENCRYPT_MODE, 8); + setExpectedOutputSize("PBEWITHMD5ANDDES", Cipher.ENCRYPT_MODE, 8); + setExpectedOutputSize("PBEWITHSHA1ANDDES", Cipher.ENCRYPT_MODE, 8); + + setExpectedOutputSize("DES", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHMD5ANDDES", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHA1ANDDES", Cipher.DECRYPT_MODE, 0); + + setExpectedOutputSize("DESEDE", Cipher.ENCRYPT_MODE, 8); + setExpectedOutputSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", Cipher.ENCRYPT_MODE, 8); + setExpectedOutputSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", Cipher.ENCRYPT_MODE, 8); + setExpectedOutputSize("PBEWITHMD5ANDTRIPLEDES", Cipher.ENCRYPT_MODE, 8); + setExpectedOutputSize("PBEWITHSHA1ANDDESEDE", Cipher.ENCRYPT_MODE, 8); + + setExpectedOutputSize("DESEDE", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHMD5ANDTRIPLEDES", Cipher.DECRYPT_MODE, 0); + setExpectedOutputSize("PBEWITHSHA1ANDDESEDE", Cipher.DECRYPT_MODE, 0); + + if (StandardNames.IS_RI) { + setExpectedOutputSize("DESEDEWRAP", Cipher.WRAP_MODE, 16); + setExpectedOutputSize("DESEDEWRAP", Cipher.UNWRAP_MODE, 0); + } else { + setExpectedOutputSize("DESEDEWRAP", -1); + } + + setExpectedOutputSize("RSA", Cipher.ENCRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.ENCRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.ENCRYPT_MODE, 256); + + setExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, 256); + setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, 245); + + // BC strips the leading 0 for us even when NoPadding is specified + setExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, "BC", 255); + setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, "BC", 255); + } + + private static void setExpectedOutputSize(String algorithm, int value) { + setExpectedSize(EXPECTED_OUTPUT_SIZE, algorithm, value); + } + + private static void setExpectedOutputSize(String algorithm, int mode, int value) { + setExpectedSize(EXPECTED_OUTPUT_SIZE, algorithm, mode, value); + } + + private static void setExpectedOutputSize(String algorithm, int mode, String provider, int value) { + setExpectedSize(EXPECTED_OUTPUT_SIZE, algorithm, mode, provider, value); + } + + private static int getExpectedOutputSize(String algorithm, int mode, String provider) { + return getExpectedSize(EXPECTED_OUTPUT_SIZE, algorithm, mode, provider); + } + + 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; + } + + private static AlgorithmParameterSpec getAlgorithmParameterSpec(String algorithm) { + if (!isPBE(algorithm)) { + return null; + } + final byte[] salt = new byte[8]; + new SecureRandom().nextBytes(salt); + return new PBEParameterSpec(salt, 1024); + } + + public void test_getInstance() throws Exception { + final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(errBuffer); + + Set<String> seenBaseCipherNames = new HashSet<String>(); + Set<String> seenCiphersWithModeAndPadding = new HashSet<String>(); + + 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(); + + /* + * Any specific modes and paddings aren't tested directly here, + * but we need to make sure we see the bare algorithm from some + * provider. We will test each mode specifically when we get the + * base cipher. + */ + final int firstSlash = algorithm.indexOf('/'); + if (firstSlash == -1) { + seenBaseCipherNames.add(algorithm); + } else { + final String baseCipherName = algorithm.substring(0, firstSlash); + if (!seenBaseCipherNames.contains(baseCipherName)) { + seenCiphersWithModeAndPadding.add(baseCipherName); + } + continue; + } + + try { + test_Cipher_Algorithm(provider, algorithm); + } catch (Throwable e) { + out.append("Error encountered checking " + algorithm + + " with provider " + provider.getName() + "\n"); + e.printStackTrace(out); + } + + Set<String> modes = StandardNames.getModesForCipher(algorithm); + if (modes != null) { + for (String mode : modes) { + Set<String> paddings = StandardNames.getPaddingsForCipher(algorithm); + if (paddings != null) { + for (String padding : paddings) { + final String algorithmName = algorithm + "/" + mode + "/" + padding; + try { + test_Cipher_Algorithm(provider, algorithmName); + } catch (Throwable e) { + out.append("Error encountered checking " + algorithmName + + " with provider " + provider.getName() + "\n"); + e.printStackTrace(out); + } + } + } + } + } + } + } + + seenCiphersWithModeAndPadding.removeAll(seenBaseCipherNames); + assertEquals("Ciphers seen with mode and padding but not base cipher", + Collections.EMPTY_SET, seenCiphersWithModeAndPadding); + + out.flush(); + if (errBuffer.size() > 0) { + throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n"); + } + } + + private void test_Cipher_Algorithm(Provider provider, String algorithm) throws Exception { + // 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); + } + + private void test_Cipher(Cipher c) throws Exception { + String algorithm = c.getAlgorithm().toUpperCase(Locale.US); + if (isUnsupported(algorithm)) { + return; + } + String providerName = c.getProvider().getName(); + String cipherID = algorithm + ":" + providerName; + + try { + c.getOutputSize(0); + } catch (IllegalStateException expected) { + } + + // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs JCERSAPrivateKey) + Key encryptKey = getEncryptKey(algorithm); + + final AlgorithmParameterSpec spec = getAlgorithmParameterSpec(algorithm); + + int encryptMode = getEncryptMode(algorithm); + c.init(encryptMode, encryptKey, spec); + assertEquals(cipherID, getExpectedBlockSize(algorithm, encryptMode, providerName), c.getBlockSize()); + assertEquals(cipherID, getExpectedOutputSize(algorithm, encryptMode, providerName), c.getOutputSize(0)); + int decryptMode = getDecryptMode(algorithm); + c.init(decryptMode, encryptKey, spec); + assertEquals(cipherID, getExpectedBlockSize(algorithm, decryptMode, providerName), c.getBlockSize()); + assertEquals(cipherID, getExpectedOutputSize(algorithm, decryptMode, providerName), c.getOutputSize(0)); + + // TODO: test Cipher.getIV() + + // TODO: test Cipher.getParameters() + + assertNull(cipherID, c.getExemptionMechanism()); + + c.init(getEncryptMode(algorithm), encryptKey, spec); + if (isWrap(algorithm)) { + byte[] cipherText = c.wrap(encryptKey); + c.init(getDecryptMode(algorithm), getDecryptKey(algorithm), spec); + 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), spec); + byte[] decryptedPlainText = c.doFinal(cipherText); + assertEquals(cipherID, + Arrays.toString(getExpectedPlainText(algorithm)), + Arrays.toString(decryptedPlainText)); + } + } + + public void testInputPKCS1Padding() throws Exception { + for (String provider : RSA_PROVIDERS) { + testInputPKCS1Padding(provider); + } + } + + private void testInputPKCS1Padding(String provider) throws Exception { + testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA")); + try { + testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA")); + fail(); + } catch (BadPaddingException expected) { + } + try { + testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA")); + fail(); + } catch (BadPaddingException expected) { + } + testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA")); + } + + private void testInputPKCS1Padding(String provider, byte[] prePaddedPlainText, Key encryptKey, Key decryptKey) throws Exception { + Cipher encryptCipher = Cipher.getInstance("RSA/ECB/NoPadding", provider); + encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey); + byte[] cipherText = encryptCipher.doFinal(prePaddedPlainText); + Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider); + 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 { + for (String provider : RSA_PROVIDERS) { + testOutputPKCS1Padding(provider); + } + } + + private void testOutputPKCS1Padding(String provider) throws Exception { + testOutputPKCS1Padding(provider, (byte) 1, getEncryptKey("RSA"), getDecryptKey("RSA")); + testOutputPKCS1Padding(provider, (byte) 2, getDecryptKey("RSA"), getEncryptKey("RSA")); + } + + private void testOutputPKCS1Padding(String provider, byte expectedBlockType, Key encryptKey, Key decryptKey) throws Exception { + Cipher encryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider); + encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey); + byte[] cipherText = encryptCipher.doFinal(ORIGINAL_PLAIN_TEXT); + Cipher decryptCipher = Cipher.getInstance("RSA/ECB/NoPadding", provider); + decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey); + byte[] plainText = decryptCipher.doFinal(cipherText); + assertPadding(provider, expectedBlockType, ORIGINAL_PLAIN_TEXT, plainText); + } + + private void assertPadding(String provider, byte expectedBlockType, byte[] expectedData, byte[] actualDataWithPadding) { + assertNotNull(provider, actualDataWithPadding); + int expectedOutputSize = getExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, provider); + assertEquals(provider, expectedOutputSize, actualDataWithPadding.length); + int expectedBlockTypeOffset; + if (provider.equals("BC")) { + // BC strips the leading 0 for us on decrypt even when NoPadding is specified... + expectedBlockTypeOffset = 0; + } else { + expectedBlockTypeOffset = 1; + assertEquals(provider, 0, actualDataWithPadding[0]); + } + byte actualBlockType = actualDataWithPadding[expectedBlockTypeOffset]; + assertEquals(provider, expectedBlockType, actualBlockType); + int actualDataOffset = actualDataWithPadding.length - expectedData.length; + if (actualBlockType == 1) { + int expectedDataOffset = expectedBlockTypeOffset + 1; + for (int i = expectedDataOffset; i < actualDataOffset - 1; i++) { + assertEquals(provider, (byte) 0xFF, actualDataWithPadding[i]); + } + } + assertEquals(provider, 0x00, actualDataWithPadding[actualDataOffset-1]); + byte[] actualData = new byte[expectedData.length]; + System.arraycopy(actualDataWithPadding, actualDataOffset, actualData, 0, actualData.length); + assertEquals(provider, 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 +882,1287 @@ public final class CipherTest extends TestCase { .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 { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success(String provider) 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", provider); + + /* + * 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 { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Private_UpdateThenEmptyDoFinal_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Private_UpdateThenEmptyDoFinal_Success(String provider) 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", provider); + + /* + * 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 { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Private_SingleByteUpdateThenEmptyDoFinal_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Private_SingleByteUpdateThenEmptyDoFinal_Success(String provider) + 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", provider); + + /* + * 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 { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Private_OnlyDoFinalWithOffset_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Private_OnlyDoFinalWithOffset_Success(String provider) 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", provider); + + /* + * 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 { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success(String provider) 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", provider); + + /* + * 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); + assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted); + + c.init(Cipher.DECRYPT_MODE, privKey); + encrypted = c.doFinal(RSA_Vector1_Encrypt_Private); + assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted); + } + + public void testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success() throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success(String provider) 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", provider); + + /* + * 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); + assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted); + + c.init(Cipher.DECRYPT_MODE, pubKey); + int decryptLen = c.doFinal(RSA_Vector1_Encrypt_Private, 0, + RSA_Vector1_Encrypt_Private.length, encrypted, 0); + if (provider.equals("BC")) { + // BC strips the leading 0 for us on decrypt even when NoPadding is specified... + decryptLen++; + encrypted = Arrays.copyOf(encrypted, encrypted.length - 1); + } + assertEquals("Encrypted size should match expected", RSA_2048_Vector1.length, decryptLen); + assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted); + } + + public void testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success() throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success(String provider) 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", provider); + + /* + * 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(); + assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted); + + c.init(Cipher.DECRYPT_MODE, privKey); + c.update(RSA_Vector1_Encrypt_Private); + encrypted = c.doFinal(); + assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted); + } + + public void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Success() + throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Success(String provider) + 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", provider); + + /* + * 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); + assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, 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); + assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted); + } + + public void testRSA_ECB_NoPadding_Public_TooSmall_Success() throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Public_TooSmall_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Public_TooSmall_Success(String provider) 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", provider); + + /* + * 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 { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Private_TooSmall_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_Private_TooSmall_Success(String provider) 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", provider); + + /* + * 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); + assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, + TooShort_Vector_Zero_Padded, encrypted); + + c.init(Cipher.DECRYPT_MODE, privKey); + encrypted = c.doFinal(RSA_Vector1_ZeroPadded_Encrypted); + assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, + TooShort_Vector_Zero_Padded, encrypted); + } + + private static void assertEncryptedEqualsNoPadding(String provider, int mode, + byte[] expected, byte[] actual) { + if (provider.equals("BC") && mode == Cipher.DECRYPT_MODE) { + // BouncyCastle does us the favor of stripping leading zeroes in DECRYPT_MODE + int nonZeroOffset = 0; + for (byte b : expected) { + if (b != 0) { + break; + } + nonZeroOffset++; + } + expected = Arrays.copyOfRange(expected, nonZeroOffset, expected.length); + } + assertEquals("Encrypted should match expected", + Arrays.toString(expected), Arrays.toString(actual)); + } + + public void testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failure() + throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failure(provider); + } + } + + private void testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failure(String provider) + 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", provider); + + /* + * 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) { + assertFalse(provider, "BC".equals(provider)); + } catch (ArrayIndexOutOfBoundsException success) { + assertEquals("BC", provider); + } + } + + public void testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failure() + throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failure(provider); + } + } + + private void testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failure(String provider) + 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", provider); + + /* + * 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) { + assertFalse(provider, "BC".equals(provider)); + } catch (ArrayIndexOutOfBoundsException success) { + assertEquals("BC", provider); + } + } + + public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure() throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure(provider); + } + } + + private void testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure(String provider) 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", provider); + + /* + * 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) { + assertFalse(provider, "BC".equals(provider)); + } catch (ArrayIndexOutOfBoundsException success) { + assertEquals("BC", provider); + } + } + + public void testRSA_ECB_NoPadding_GetBlockSize_Success() throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_GetBlockSize_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_GetBlockSize_Success(String provider) throws Exception { + Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); + if (StandardNames.IS_RI) { + assertEquals(0, c.getBlockSize()); + } else { + try { + c.getBlockSize(); + fail(); + } catch (IllegalStateException expected) { + } + } + + 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(getExpectedBlockSize("RSA", Cipher.ENCRYPT_MODE, provider), c.getBlockSize()); + } + + public void testRSA_ECB_NoPadding_GetOutputSize_NoInit_Failure() throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_GetOutputSize_NoInit_Failure(provider); + } + } + + private void testRSA_ECB_NoPadding_GetOutputSize_NoInit_Failure(String provider) throws Exception { + Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider); + 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 { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_GetOutputSize_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_GetOutputSize_Success(String provider) 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", provider); + c.init(Cipher.ENCRYPT_MODE, pubKey); + + final int modulusInBytes = RSA_2048_modulus.bitLength() / 8; + assertEquals(modulusInBytes, c.getOutputSize(RSA_2048_Vector1.length)); + assertEquals(modulusInBytes, c.getOutputSize(RSA_2048_Vector1.length * 2)); + assertEquals(modulusInBytes, c.getOutputSize(0)); + } + + public void testRSA_ECB_NoPadding_GetIV_Success() throws Exception { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_GetIV_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_GetIV_Success(String provider) 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", provider); + 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 { + for (String provider : RSA_PROVIDERS) { + testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(provider); + } + } + + private void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(String provider) 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", provider); + assertNull("Parameters should be null", c.getParameters()); + } + + /* + * Test vector generation: + * openssl rand -hex 16 + * echo '3d4f8970b1f27537f40a39298a41555f' | sed 's/\(..\)/(byte) 0x\1, /g' + */ + private static final byte[] AES_128_KEY = new byte[] { + (byte) 0x3d, (byte) 0x4f, (byte) 0x89, (byte) 0x70, (byte) 0xb1, (byte) 0xf2, + (byte) 0x75, (byte) 0x37, (byte) 0xf4, (byte) 0x0a, (byte) 0x39, (byte) 0x29, + (byte) 0x8a, (byte) 0x41, (byte) 0x55, (byte) 0x5f, + }; + + /* + * Test key generation: + * openssl rand -hex 24 + * echo '5a7a3d7e40b64ed996f7afa15f97fd595e27db6af428e342' | sed 's/\(..\)/(byte) 0x\1, /g' + */ + private static final byte[] AES_192_KEY = new byte[] { + (byte) 0x5a, (byte) 0x7a, (byte) 0x3d, (byte) 0x7e, (byte) 0x40, (byte) 0xb6, + (byte) 0x4e, (byte) 0xd9, (byte) 0x96, (byte) 0xf7, (byte) 0xaf, (byte) 0xa1, + (byte) 0x5f, (byte) 0x97, (byte) 0xfd, (byte) 0x59, (byte) 0x5e, (byte) 0x27, + (byte) 0xdb, (byte) 0x6a, (byte) 0xf4, (byte) 0x28, (byte) 0xe3, (byte) 0x42, + }; + + /* + * Test key generation: + * openssl rand -hex 32 + * echo 'ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935' | sed 's/\(..\)/(byte) 0x\1, /g' + */ + private static final byte[] AES_256_KEY = new byte[] { + (byte) 0xec, (byte) 0x53, (byte) 0xc6, (byte) 0xd5, (byte) 0x1d, (byte) 0x2c, + (byte) 0x49, (byte) 0x73, (byte) 0x58, (byte) 0x5f, (byte) 0xb0, (byte) 0xb8, + (byte) 0xe5, (byte) 0x1c, (byte) 0xd2, (byte) 0xe3, (byte) 0x99, (byte) 0x15, + (byte) 0xff, (byte) 0x07, (byte) 0xa1, (byte) 0x83, (byte) 0x78, (byte) 0x72, + (byte) 0x71, (byte) 0x5d, (byte) 0x61, (byte) 0x21, (byte) 0xbf, (byte) 0x86, + (byte) 0x19, (byte) 0x35, + }; + + private static final byte[][] AES_KEYS = new byte[][] { + AES_128_KEY, AES_192_KEY, AES_256_KEY, + }; + + private static final String[] AES_MODES = new String[] { + "AES/ECB", + "AES/CBC", + "AES/CFB", + "AES/CTR", + "AES/OFB", + }; + + /* + * Test vector creation: + * echo -n 'Hello, world!' | recode ../x1 | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext = new byte[] { + (byte) 0x48, (byte) 0x65, (byte) 0x6C, (byte) 0x6C, (byte) 0x6F, (byte) 0x2C, + (byte) 0x20, (byte) 0x77, (byte) 0x6F, (byte) 0x72, (byte) 0x6C, (byte) 0x64, + (byte) 0x21, + }; + + /* + * Test vector creation: + * openssl enc -aes-128-ecb -K 3d4f8970b1f27537f40a39298a41555f -in blah|openssl enc -aes-128-ecb -K 3d4f8970b1f27537f40a39298a41555f -nopad -d|recode ../x1 | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded = new byte[] { + (byte) 0x48, (byte) 0x65, (byte) 0x6C, (byte) 0x6C, (byte) 0x6F, (byte) 0x2C, + (byte) 0x20, (byte) 0x77, (byte) 0x6F, (byte) 0x72, (byte) 0x6C, (byte) 0x64, + (byte) 0x21, (byte) 0x03, (byte) 0x03, (byte) 0x03 + }; + + /* + * Test vector generation: + * openssl enc -aes-128-ecb -K 3d4f8970b1f27537f40a39298a41555f -in blah|recode ../x1 | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted = new byte[] { + (byte) 0x65, (byte) 0x3E, (byte) 0x86, (byte) 0xFB, (byte) 0x05, (byte) 0x5A, + (byte) 0x52, (byte) 0xEA, (byte) 0xDD, (byte) 0x08, (byte) 0xE7, (byte) 0x48, + (byte) 0x33, (byte) 0x01, (byte) 0xFC, (byte) 0x5A, + }; + + /* + * Test key generation: + * openssl rand -hex 16 + * echo 'ceaa31952dfd3d0f5af4b2042ba06094' | sed 's/\(..\)/(byte) 0x\1, /g' + */ + private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_IV = new byte[] { + (byte) 0xce, (byte) 0xaa, (byte) 0x31, (byte) 0x95, (byte) 0x2d, (byte) 0xfd, + (byte) 0x3d, (byte) 0x0f, (byte) 0x5a, (byte) 0xf4, (byte) 0xb2, (byte) 0x04, + (byte) 0x2b, (byte) 0xa0, (byte) 0x60, (byte) 0x94, + }; + + /* + * Test vector generation: + * echo -n 'I only regret that I have but one test to write.' | recode ../x1 | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext = new byte[] { + (byte) 0x49, (byte) 0x20, (byte) 0x6F, (byte) 0x6E, (byte) 0x6C, (byte) 0x79, + (byte) 0x20, (byte) 0x72, (byte) 0x65, (byte) 0x67, (byte) 0x72, (byte) 0x65, + (byte) 0x74, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x61, (byte) 0x74, + (byte) 0x20, (byte) 0x49, (byte) 0x20, (byte) 0x68, (byte) 0x61, (byte) 0x76, + (byte) 0x65, (byte) 0x20, (byte) 0x62, (byte) 0x75, (byte) 0x74, (byte) 0x20, + (byte) 0x6F, (byte) 0x6E, (byte) 0x65, (byte) 0x20, (byte) 0x74, (byte) 0x65, + (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x74, (byte) 0x6F, (byte) 0x20, + (byte) 0x77, (byte) 0x72, (byte) 0x69, (byte) 0x74, (byte) 0x65, (byte) 0x2E + }; + + /* + * Test vector generation: + * echo -n 'I only regret that I have but one test to write.' | openssl enc -aes-256-cbc -K ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935 -iv ceaa31952dfd3d0f5af4b2042ba06094 | openssl enc -aes-256-cbc -K ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935 -iv ceaa31952dfd3d0f5af4b2042ba06094 -d -nopad | recode ../x1 | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded = new byte[] { + (byte) 0x49, (byte) 0x20, (byte) 0x6F, (byte) 0x6E, (byte) 0x6C, (byte) 0x79, + (byte) 0x20, (byte) 0x72, (byte) 0x65, (byte) 0x67, (byte) 0x72, (byte) 0x65, + (byte) 0x74, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x61, (byte) 0x74, + (byte) 0x20, (byte) 0x49, (byte) 0x20, (byte) 0x68, (byte) 0x61, (byte) 0x76, + (byte) 0x65, (byte) 0x20, (byte) 0x62, (byte) 0x75, (byte) 0x74, (byte) 0x20, + (byte) 0x6F, (byte) 0x6E, (byte) 0x65, (byte) 0x20, (byte) 0x74, (byte) 0x65, + (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x74, (byte) 0x6F, (byte) 0x20, + (byte) 0x77, (byte) 0x72, (byte) 0x69, (byte) 0x74, (byte) 0x65, (byte) 0x2E, + (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, + (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, + (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10 + }; + + /* + * Test vector generation: + * echo -n 'I only regret that I have but one test to write.' | openssl enc -aes-256-cbc -K ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935 -iv ceaa31952dfd3d0f5af4b2042ba06094 | recode ../x1 | sed 's/0x/(byte) 0x/g' + */ + private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext = new byte[] { + (byte) 0x90, (byte) 0x65, (byte) 0xDD, (byte) 0xAF, (byte) 0x7A, (byte) 0xCE, + (byte) 0xAE, (byte) 0xBF, (byte) 0xE8, (byte) 0xF6, (byte) 0x9E, (byte) 0xDB, + (byte) 0xEA, (byte) 0x65, (byte) 0x28, (byte) 0xC4, (byte) 0x9A, (byte) 0x28, + (byte) 0xEA, (byte) 0xA3, (byte) 0x95, (byte) 0x2E, (byte) 0xFF, (byte) 0xF1, + (byte) 0xA0, (byte) 0xCA, (byte) 0xC2, (byte) 0xA4, (byte) 0x65, (byte) 0xCD, + (byte) 0xBF, (byte) 0xCE, (byte) 0x9E, (byte) 0xF1, (byte) 0x57, (byte) 0xF6, + (byte) 0x32, (byte) 0x2E, (byte) 0x8F, (byte) 0x93, (byte) 0x2E, (byte) 0xAE, + (byte) 0x41, (byte) 0x33, (byte) 0x54, (byte) 0xD0, (byte) 0xEF, (byte) 0x8C, + (byte) 0x52, (byte) 0x14, (byte) 0xAC, (byte) 0x2D, (byte) 0xD5, (byte) 0xA4, + (byte) 0xF9, (byte) 0x20, (byte) 0x77, (byte) 0x25, (byte) 0x91, (byte) 0x3F, + (byte) 0xD1, (byte) 0xB9, (byte) 0x00, (byte) 0x3E + }; + + private static class CipherTestParam { + public final String mode; + + public final byte[] key; + + public final byte[] iv; + + public final byte[] plaintext; + + public final byte[] ciphertext; + + public final byte[] plaintextPadded; + + public CipherTestParam(String mode, byte[] key, byte[] iv, byte[] plaintext, + byte[] plaintextPadded, byte[] ciphertext) { + this.mode = mode; + this.key = key; + this.iv = iv; + this.plaintext = plaintext; + this.plaintextPadded = plaintextPadded; + this.ciphertext = ciphertext; + } + } + + private static List<CipherTestParam> CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>(); + static { + CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB", AES_128_KEY, + null, + AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext, + AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded, + AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted)); + if (IS_UNLIMITED) { + CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC", AES_256_KEY, + AES_256_CBC_PKCS5Padding_TestVector_1_IV, + AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext, + AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded, + AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext)); + } + } + + public void testCipher_Success() throws Exception { + for (String provider : AES_PROVIDERS) { + testCipher_Success(provider); + } + } + + private void testCipher_Success(String provider) throws Exception { + final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(errBuffer); + for (CipherTestParam p : CIPHER_TEST_PARAMS) { + try { + checkCipher(p, provider); + } catch (Exception e) { + out.append("Error encountered checking " + p.mode + ", keySize=" + + (p.key.length * 8) + "\n"); + e.printStackTrace(out); + } + } + out.flush(); + if (errBuffer.size() > 0) { + throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n"); + } + } + + private void checkCipher(CipherTestParam p, String provider) throws Exception { + SecretKey key = new SecretKeySpec(p.key, "AES"); + Cipher c = Cipher.getInstance(p.mode + "/PKCS5Padding", provider); + AlgorithmParameterSpec spec = null; + if (p.iv != null) { + spec = new IvParameterSpec(p.iv); + } + c.init(Cipher.ENCRYPT_MODE, key, spec); + + final byte[] actualCiphertext = c.doFinal(p.plaintext); + assertTrue(Arrays.equals(p.ciphertext, actualCiphertext)); + + c.init(Cipher.DECRYPT_MODE, key, spec); + + final byte[] actualPlaintext = c.doFinal(p.ciphertext); + assertTrue(Arrays.equals(p.plaintext, actualPlaintext)); + + Cipher cNoPad = Cipher.getInstance(p.mode + "/NoPadding", provider); + cNoPad.init(Cipher.DECRYPT_MODE, key, spec); + + final byte[] actualPlaintextPadded = cNoPad.doFinal(p.ciphertext); + assertTrue(Arrays.equals(p.plaintextPadded, actualPlaintextPadded)); + } + + public void testCipher_ShortBlock_Failure() throws Exception { + for (String provider : AES_PROVIDERS) { + testCipher_ShortBlock_Failure(provider); + } + } + + private void testCipher_ShortBlock_Failure(String provider) throws Exception { + final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(errBuffer); + for (CipherTestParam p : CIPHER_TEST_PARAMS) { + try { + checkCipher_ShortBlock_Failure(p, provider); + } catch (Exception e) { + out.append("Error encountered checking " + p.mode + ", keySize=" + + (p.key.length * 8) + "\n"); + e.printStackTrace(out); + } + } + out.flush(); + if (errBuffer.size() > 0) { + throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n"); + } + } + + private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) throws Exception { + SecretKey key = new SecretKeySpec(p.key, "AES"); + Cipher c = Cipher.getInstance(p.mode + "/NoPadding", provider); + if (c.getBlockSize() == 0) { + return; + } + + c.init(Cipher.ENCRYPT_MODE, key); + try { + c.doFinal(new byte[] { 0x01, 0x02, 0x03 }); + fail("Should throw IllegalBlockSizeException on wrong-sized block"); + } catch (IllegalBlockSizeException expected) { + } + } + + public void testAES_ECB_PKCS5Padding_ShortBuffer_Failure() throws Exception { + for (String provider : AES_PROVIDERS) { + testAES_ECB_PKCS5Padding_ShortBuffer_Failure(provider); + } + } + + private void testAES_ECB_PKCS5Padding_ShortBuffer_Failure(String provider) throws Exception { + SecretKey key = new SecretKeySpec(AES_128_KEY, "AES"); + Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding", provider); + c.init(Cipher.ENCRYPT_MODE, key); + + final byte[] fragmentOutput = c.update(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext); + if (fragmentOutput != null) { + assertEquals(0, fragmentOutput.length); + } + + // Provide null buffer. + { + try { + c.doFinal(null, 0); + fail("Should throw NullPointerException on null output buffer"); + } catch (NullPointerException expected) { + } catch (IllegalArgumentException expected) { + } + } + + // Provide short buffer. + { + final byte[] output = new byte[c.getBlockSize() - 1]; + try { + c.doFinal(output, 0); + fail("Should throw ShortBufferException on short output buffer"); + } catch (ShortBufferException expected) { + } + } + + // Start 1 byte into output buffer. + { + final byte[] output = new byte[c.getBlockSize()]; + try { + c.doFinal(output, 1); + fail("Should throw ShortBufferException on short output buffer"); + } catch (ShortBufferException expected) { + } + } + + // Should keep data for real output buffer + { + final byte[] output = new byte[c.getBlockSize()]; + assertEquals(AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted.length, c.doFinal(output, 0)); + assertTrue(Arrays.equals(AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted, output)); + } + } + + public void testAES_ECB_NoPadding_IncrementalUpdate_Success() throws Exception { + for (String provider : AES_PROVIDERS) { + testAES_ECB_NoPadding_IncrementalUpdate_Success(provider); + } + } + + private void testAES_ECB_NoPadding_IncrementalUpdate_Success(String provider) throws Exception { + SecretKey key = new SecretKeySpec(AES_128_KEY, "AES"); + Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider); + c.init(Cipher.ENCRYPT_MODE, key); + + for (int i = 0; i < AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded.length - 1; i++) { + final byte[] outputFragment = c.update(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded, i, 1); + if (outputFragment != null) { + assertEquals(0, outputFragment.length); + } + } + + final byte[] output = c.doFinal(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded, + AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded.length - 1, 1); + assertNotNull(output); + assertEquals(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded.length, output.length); + + assertTrue(Arrays.equals(AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted, output)); + } + + private static final byte[] AES_IV_ZEROES = 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, + }; + + public void testAES_ECB_NoPadding_IvParameters_Failure() throws Exception { + for (String provider : AES_PROVIDERS) { + testAES_ECB_NoPadding_IvParameters_Failure(provider); + } + } + + private void testAES_ECB_NoPadding_IvParameters_Failure(String provider) throws Exception { + SecretKey key = new SecretKeySpec(AES_128_KEY, "AES"); + Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider); + + AlgorithmParameterSpec spec = new IvParameterSpec(AES_IV_ZEROES); + try { + c.init(Cipher.ENCRYPT_MODE, key, spec); + fail("Should not accept an IV in ECB mode"); + } catch (InvalidAlgorithmParameterException expected) { + } + } } 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 e3ae16f..65d8690 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java @@ -347,6 +347,51 @@ public class SSLEngineTest extends TestCase { 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/SSLSessionTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java index 217dfe9..67c83bf 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java @@ -54,11 +54,24 @@ public class SSLSessionTest extends TestCase { } public void test_SSLSession_getCreationTime() { + // We use OpenSSL, which only returns times accurate to the nearest second. + // NativeCrypto just multiplies by 1000, which looks like truncation, which + // would make it appear as if the OpenSSL side of things was created before + // we called it. + long t0 = System.currentTimeMillis() / 1000; TestSSLSessions s = TestSSLSessions.create(); + long t1 = System.currentTimeMillis() / 1000; + assertTrue(s.invalid.getCreationTime() > 0); - assertTrue(s.server.getCreationTime() > 0); - assertTrue(s.client.getCreationTime() > 0); - assertTrue(Math.abs(s.server.getCreationTime() - s.client.getCreationTime()) < 1 * 1000); + + long sTime = s.server.getCreationTime() / 1000; + assertTrue(sTime + " >= " + t0, sTime >= t0); + assertTrue(sTime + " <= " + t1, sTime <= t1); + + long cTime = s.client.getCreationTime() / 1000; + assertTrue(cTime + " >= " + t0, cTime >= t0); + assertTrue(cTime + " <= " + t1, cTime <= t1); + s.close(); } 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..4095081 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java @@ -16,8 +16,10 @@ package libcore.javax.net.ssl; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; @@ -30,6 +32,10 @@ import java.security.cert.X509Certificate; 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 +44,6 @@ import javax.net.ssl.SSLException; 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 +276,28 @@ public class SSLSocketTest extends TestCase { 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 +308,7 @@ public class SSLSocketTest extends TestCase { TestSSLContext.assertServerCertificateChain(c.clientTrustManager, peerCertificates); TestSSLContext.assertCertificateInKeyStore(peerCertificates[0], c.serverKeyStore); - thread.join(); + future.get(); client.close(); server.close(); c.close(); @@ -321,25 +322,24 @@ public class SSLSocketTest extends TestCase { // 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 +359,16 @@ public class SSLSocketTest extends TestCase { 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 +379,14 @@ public class SSLSocketTest extends TestCase { 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 +464,7 @@ public class SSLSocketTest extends TestCase { } }); client.startHandshake(); - thread.join(); + future.get(); if (!TestSSLContext.sslServerSocketSupportsSessionTickets()) { assertNotNull(c.serverContext.getServerSessionContext().getSession( client.getSession().getId())); @@ -492,25 +484,21 @@ public class SSLSocketTest extends TestCase { 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,22 +539,6 @@ public class SSLSocketTest extends TestCase { } } - public void test_SSLSocket_setUseClientMode_afterHandshake() throws Exception { - - // can't set after handshake - TestSSLEnginePair pair = TestSSLEnginePair.create(null); - try { - pair.server.setUseClientMode(false); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - pair.client.setUseClientMode(false); - fail(); - } catch (IllegalArgumentException expected) { - } - } - private void test_SSLSocket_setUseClientMode(final boolean clientClientMode, final boolean serverClientMode) throws Exception { @@ -575,64 +547,73 @@ public class SSLSocketTest extends TestCase { 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() { + 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) { - sslHandshakeException[0] = e; + return e; } catch (SocketTimeoutException e) { - socketTimeoutException[0] = e; - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); + return e; } } }); - thread.start(); + executor.shutdown(); 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]; + 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 + TestSSLEnginePair pair = TestSSLEnginePair.create(null); + try { + pair.server.setUseClientMode(false); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + pair.client.setUseClientMode(false); + fail(); + } catch (IllegalArgumentException expected) { + } + } + 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 +622,7 @@ public class SSLSocketTest extends TestCase { } client.close(); server.close(); - thread.join(); + future.get(); } public void test_SSLSocket_clientAuth() throws Exception { @@ -650,43 +631,38 @@ public class SSLSocketTest extends TestCase { 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()); - - // 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 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); - } + 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 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()); + + 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 +703,20 @@ public class SSLSocketTest extends TestCase { 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 +724,7 @@ public class SSLSocketTest extends TestCase { // 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 +747,25 @@ public class SSLSocketTest extends TestCase { 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 +776,25 @@ public class SSLSocketTest extends TestCase { 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 +978,25 @@ public class SSLSocketTest extends TestCase { 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 +1013,10 @@ public class SSLSocketTest extends TestCase { assertEquals(0, wrapping.getSoTimeout()); // setting wrapper sets underlying and ... - wrapping.setSoTimeout(10); - assertEquals(10, wrapping.getSoTimeout()); - assertEquals(10, underlying.getSoTimeout()); + int expectedTimeoutMillis = 1000; // 10 was too small because it was affected by rounding + wrapping.setSoTimeout(expectedTimeoutMillis); + assertEquals(expectedTimeoutMillis, wrapping.getSoTimeout()); + assertEquals(expectedTimeoutMillis, underlying.getSoTimeout()); // ... getting wrapper inspects underlying underlying.setSoTimeout(0); @@ -1091,6 +1051,52 @@ public class SSLSocketTest extends TestCase { listening.close(); } + public void test_SSLSocket_setSoWriteTimeout() throws Exception { + if (StandardNames.IS_RI) { + // RI does not support write timeout on sockets + return; + } + + final 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<Void> future = executor.submit(new Callable<Void>() { + @Override public Void call() throws Exception { + server.startHandshake(); + return null; + } + }); + executor.shutdown(); + client.startHandshake(); + + // Reflection is used so this can compile on the RI + String expectedClassName = "org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl"; + Class actualClass = client.getClass(); + assertEquals(expectedClassName, actualClass.getName()); + Method setSoWriteTimeout = actualClass.getMethod("setSoWriteTimeout", + new Class[] { Integer.TYPE }); + setSoWriteTimeout.invoke(client, 1); + + // Try to make the size smaller (it can be 512k or even megabytes). + // Note that it may not respect your request, so read back the actual value. + int sendBufferSize = 1024; + client.setSendBufferSize(sendBufferSize); + sendBufferSize = client.getSendBufferSize(); + + try { + client.getOutputStream().write(new byte[sendBufferSize + 1]); + fail(); + } catch (SocketTimeoutException expected) { + } + + future.get(); + client.close(); + server.close(); + c.close(); + } + public void test_SSLSocket_interrupt() throws Exception { ServerSocket listening = new ServerSocket(0); @@ -1126,17 +1132,15 @@ public class SSLSocketTest extends TestCase { 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 +1149,43 @@ public class SSLSocketTest extends TestCase { 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 @@ public final class HttpResponseCacheTest extends TestCase { 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 @@ public final class HttpResponseCacheTest extends TestCase { 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 @@ public final class HttpResponseCacheTest extends TestCase { 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 @@ public final class HttpResponseCacheTest extends TestCase { } } - 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 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 @@ public class KXmlSerializerTest extends TestCase { } 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 @@ public class AnnotationTest extends TestCase { 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/archive/tests/java/util/zip/ZipFileTest.java b/luni/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java index 17d251b..a423f22 100644 --- a/luni/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java +++ b/luni/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java @@ -32,6 +32,7 @@ import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; +import libcore.java.lang.ref.FinalizationTester; public class ZipFileTest extends junit.framework.TestCase { @@ -150,11 +151,8 @@ public class ZipFileTest extends junit.framework.TestCase { * entry1); entry1 = null; zip = null; */ - assertNotNull("Did not find entry", - test_finalize1(test_finalize2(file))); - System.gc(); - System.gc(); - System.runFinalization(); + assertNotNull("Did not find entry", test_finalize1(test_finalize2(file))); + FinalizationTester.induceFinalization(); file.delete(); assertTrue("Zip should not exist", !file.exists()); } diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherTest.java index 7442210..c07b180 100644 --- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherTest.java +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/CipherTest.java @@ -47,6 +47,7 @@ import javax.crypto.ShortBufferException; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import libcore.java.security.StandardNames; import org.apache.harmony.crypto.tests.support.MyCipher; import tests.support.resource.Support_Resources; @@ -545,7 +546,10 @@ public class CipherTest extends junit.framework.TestCase { try { c.doFinal(b, 3, 6, b1, 5); fail(); - } catch (ShortBufferException expected) { + } catch (IllegalBlockSizeException maybeExpected) { + assertTrue(StandardNames.IS_RI); + } catch (ShortBufferException maybeExpected) { + assertFalse(StandardNames.IS_RI); } } 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.io.Serializable; 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.SealedObject; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import libcore.util.SerializationTester; + /** */ public class SealedObjectTest extends TestCase { @@ -291,4 +294,23 @@ public class SealedObjectTest extends TestCase { } } + // 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 @@ public class CipherRSATest extends TestCase { 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 @@ public class CipherRSAThread extends CipherThread { 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 abstract class CipherThread implements Runnable { } } + 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 3ca11f7..07e3de5 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 @@ package org.apache.harmony.luni.tests.java.net; 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 class URLConnectionTest extends TestCase { 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/KeyRepTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyRepTest.java index 0dad827..a6529e8 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyRepTest.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyRepTest.java @@ -26,199 +26,146 @@ import java.io.NotSerializableException; import java.io.ObjectStreamException; import java.security.KeyRep; import java.security.Security; -import java.util.Iterator; import java.util.Set; - import junit.framework.TestCase; -/** - * - * - */ public class KeyRepTest extends TestCase { - private static final Set<String> keyFactoryAlgorithm; + private static final Set<String> keyFactoryAlgorithms = Security.getAlgorithms("KeyFactory"); static { - keyFactoryAlgorithm = Security.getAlgorithms("KeyFactory"); + assertFalse(keyFactoryAlgorithms.isEmpty()); } public final void testKeyRep01() { - try { - assertNotNull(new KeyRep(KeyRep.Type.SECRET, "", "", new byte[] {})); - } catch (Exception e) { - fail("Unexpected exception " + e.getMessage()); - } - - try { - assertNotNull(new KeyRep(KeyRep.Type.PUBLIC, "", "", new byte[] {})); - } catch (Exception e) { - fail("Unexpected exception " + e.getMessage()); - } - - try { - assertNotNull(new KeyRep(KeyRep.Type.PRIVATE, "", "", new byte[] {})); - } catch (Exception e) { - fail("Unexpected exception " + e.getMessage()); - } + assertNotNull(new KeyRep(KeyRep.Type.SECRET, "", "", new byte[] {})); + assertNotNull(new KeyRep(KeyRep.Type.PUBLIC, "", "", new byte[] {})); + assertNotNull(new KeyRep(KeyRep.Type.PRIVATE, "", "", new byte[] {})); } public final void testKeyRep02() { try { new KeyRep(null, "", "", new byte[] {}); fail("NullPointerException has not been thrown (type)"); - } catch (NullPointerException ok) { - + } catch (NullPointerException expected) { } try { new KeyRep(KeyRep.Type.SECRET, null, "", new byte[] {}); fail("NullPointerException has not been thrown (alg)"); - } catch (NullPointerException ok) { - + } catch (NullPointerException expected) { } try { new KeyRep(KeyRep.Type.PRIVATE, "", null, new byte[] {}); fail("NullPointerException has not been thrown (format)"); - } catch (NullPointerException ok) { - + } catch (NullPointerException expected) { } try { new KeyRep(KeyRep.Type.PUBLIC, "", "", null); fail("NullPointerException has not been thrown (encoding)"); - } catch (NullPointerException ok) { - + } catch (NullPointerException expected) { } } - public final void testReadResolve01() throws ObjectStreamException { - KeyRepChild kr = new KeyRepChild(KeyRep.Type.SECRET, "", "", - new byte[] {}); + public final void testReadResolve01() throws Exception { + KeyRepChild kr = new KeyRepChild(KeyRep.Type.SECRET, "", "", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (no format)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } kr = new KeyRepChild(KeyRep.Type.SECRET, "", "X.509", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (unacceptable format)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } kr = new KeyRepChild(KeyRep.Type.SECRET, "", "RAW", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (empty key)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } } - public final void testReadResolve02() throws ObjectStreamException { - KeyRepChild kr = new KeyRepChild(KeyRep.Type.PUBLIC, "", "", - new byte[] {}); + public final void testReadResolve02() throws Exception { + KeyRepChild kr = new KeyRepChild(KeyRep.Type.PUBLIC, "", "", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (no format)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } kr = new KeyRepChild(KeyRep.Type.PUBLIC, "", "RAW", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (unacceptable format)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } - kr = new KeyRepChild(KeyRep.Type.PUBLIC, "bla-bla", "X.509", - new byte[] {}); + kr = new KeyRepChild(KeyRep.Type.PUBLIC, "bla-bla", "X.509", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (unknown KeyFactory algorithm)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } } - public final void testReadResolve03() throws ObjectStreamException { - KeyRepChild kr = new KeyRepChild(KeyRep.Type.PRIVATE, "", "", - new byte[] {}); + public final void testReadResolve03() throws Exception { + KeyRepChild kr = new KeyRepChild(KeyRep.Type.PRIVATE, "", "", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (no format)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } kr = new KeyRepChild(KeyRep.Type.PRIVATE, "", "RAW", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (unacceptable format)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } - kr = new KeyRepChild(KeyRep.Type.PRIVATE, "bla-bla", "PKCS#8", - new byte[] {}); + kr = new KeyRepChild(KeyRep.Type.PRIVATE, "bla-bla", "PKCS#8", new byte[] {}); try { kr.readResolve(); fail("NotSerializableException has not been thrown (unknown KeyFactory algorithm)"); - } catch (NotSerializableException ok) { - + } catch (NotSerializableException expected) { } } - public final void testReadResolve04() throws ObjectStreamException { - if (keyFactoryAlgorithm.isEmpty()) { - System.err.println(getName() - + ": skipped - no KeyFactory algorithms available"); - return; - } else { - } - for (Iterator<String> i = keyFactoryAlgorithm.iterator(); i.hasNext();) { - KeyRepChild kr = new KeyRepChild(KeyRep.Type.PUBLIC, i.next(), - "X.509", new byte[] { 1, 2, 3 }); + public final void testReadResolve04() throws Exception { + for (String algorithm : keyFactoryAlgorithms) { + KeyRepChild kr = new KeyRepChild(KeyRep.Type.PUBLIC, algorithm, "X.509", + new byte[] { 1, 2, 3 }); try { kr.readResolve(); - fail("NotSerializableException has not been thrown (no format)"); - } catch (NotSerializableException ok) { - + fail("NotSerializableException has not been thrown (no format) " + algorithm); + } catch (NotSerializableException expected) { } } } - public final void testReadResolve05() throws ObjectStreamException { - if (keyFactoryAlgorithm.isEmpty()) { - System.err.println(getName() - + ": skipped - no KeyFactory algorithms available"); - return; - } else { - } - for (Iterator<String> i = keyFactoryAlgorithm.iterator(); i.hasNext();) { - KeyRepChild kr = new KeyRepChild(KeyRep.Type.PRIVATE, i.next(), - "PKCS#8", new byte[] { 1, 2, 3 }); + public final void testReadResolve05() throws Exception { + for (String algorithm : keyFactoryAlgorithms) { + KeyRepChild kr = new KeyRepChild(KeyRep.Type.PRIVATE, algorithm, "PKCS#8", + new byte[] { 1, 2, 3 }); try { kr.readResolve(); - fail("NotSerializableException has not been thrown (no format)"); - } catch (NotSerializableException ok) { - + fail("NotSerializableException has not been thrown (no format) " + algorithm); + } catch (NotSerializableException expected) { } } } class KeyRepChild extends KeyRep { - public KeyRepChild(KeyRep.Type type, String algorithm, String format, - byte[] encoded) { + public KeyRepChild(KeyRep.Type type, String algorithm, String format, byte[] encoded) { super(type, algorithm, format, encoded); } - public Object readResolve() throws ObjectStreamException { + // Overriden to make public for testing + @Override public Object readResolve() throws ObjectStreamException { return super.readResolve(); } - } } 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.Calendar; 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 @@ public class KeyStore2Test extends junit.framework.TestCase { 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/KeyStoreLoadStoreParameterTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreLoadStoreParameterTest.java deleted file mode 100644 index 81ec30d..0000000 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStoreLoadStoreParameterTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.apache.harmony.security.tests.java.security; - -import java.security.KeyStore; - -public class KeyStoreLoadStoreParameterTest { - - class MyLoadStoreParameter implements KeyStore.LoadStoreParameter { - public KeyStore.ProtectionParameter getProtectionParameter() { - return null; - } - } - - - -} 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.PublicKey; 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 @@ public class KeyStoreSpiTest extends TestCase { 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; } + }; + + BadKeyStoreEntry badEntry = new BadKeyStoreEntry(); + BadKeyStoreProtectionParameter badParameter = new BadKeyStoreProtectionParameter(); - tmpEntry entry = new tmpEntry(); - tmpProtection pPar = new tmpProtection(); + 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 @@ public class KeyStoreSpiTest extends TestCase { 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 @@ public class KeyStoreSpiTest extends TestCase { try { ksSpi.engineStore(null, null); fail("IOException must be thrown"); - } catch (IOException e) { + } catch (IOException expected) { } } @@ -202,35 +206,30 @@ public class KeyStoreSpiTest extends TestCase { 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 @@ public class SecureRandom2Test extends TestCase { * 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 @@ public class SecureRandom2Test extends TestCase { * 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/security/tests/java/security/Security2Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/Security2Test.java index d9f4dd7..68e7cbc 100644 --- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/Security2Test.java +++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/Security2Test.java @@ -20,14 +20,14 @@ package org.apache.harmony.security.tests.java.security; import java.security.InvalidParameterException; import java.security.Provider; import java.security.Security; -import java.util.Hashtable; -import java.util.Iterator; +import java.util.HashMap; import java.util.Map; import java.util.Set; +import junit.framework.TestCase; import tests.support.Support_ProviderTrust; import tests.support.Support_TestProvider; -public class Security2Test extends junit.framework.TestCase { +public class Security2Test extends TestCase { /** * java.security.Security#getProviders(java.lang.String) @@ -36,16 +36,13 @@ public class Security2Test extends junit.framework.TestCase { // Test for method void // java.security.Security.getProviders(java.lang.String) - Hashtable<String, Integer> allSupported = new Hashtable<String, Integer>(); + Map<String, Integer> allSupported = new HashMap<String, Integer>(); Provider[] allProviders = Security.getProviders(); // Add all non-alias entries to allSupported - for (int i = 0; i < allProviders.length; i++) { - Provider provider = allProviders[i]; - Iterator it = provider.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = (Map.Entry) it.next(); - String key = (String) entry.getKey(); + for (Provider provider : allProviders) { + for (Object k : provider.keySet()) { + String key = (String) k; // No aliases and no provider data if (!isAlias(key) && !isProviderData(key)) { addOrIncrementTable(allSupported, key); @@ -56,22 +53,18 @@ public class Security2Test extends junit.framework.TestCase { // Now walk through aliases. If an alias has actually been added // to the allSupported table then increment the count of the // entry that is being aliased. - for (int i = 0; i < allProviders.length; i++) { - Provider provider = allProviders[i]; - Iterator it = provider.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = (Map.Entry) it.next(); + for (Provider provider : allProviders) { + for (Map.Entry entry : provider.entrySet()) { String key = (String) entry.getKey(); if (isAlias(key)) { String aliasVal = key.substring("ALG.ALIAS.".length()); - String aliasKey = aliasVal.substring(0, aliasVal - .indexOf(".") + 1) + String aliasKey = aliasVal.substring(0, aliasVal.indexOf(".") + 1) + entry.getValue(); // Skip over nonsense alias declarations where alias and // aliased are identical. Such entries can occur. - if (!aliasVal.equals(aliasKey)) { + if (!aliasVal.equalsIgnoreCase(aliasKey)) { // Has a real entry been added for aliasValue ? - if (allSupported.containsKey(aliasVal)) { + if (allSupported.containsKey(aliasVal.toUpperCase())) { // Add 1 to the provider count of the thing being // aliased addOrIncrementTable(allSupported, aliasKey); @@ -81,17 +74,13 @@ public class Security2Test extends junit.framework.TestCase { }// end while more entries }// end for all providers - Provider provTest[] = null; - Iterator it = allSupported.keySet().iterator(); - while (it.hasNext()) { - String filterString = (String) it.next(); + for (String filterString : allSupported.keySet()) { try { - provTest = Security.getProviders(filterString); - int expected = ((Integer) allSupported.get(filterString)) - .intValue(); - assertEquals( - "Unexpected number of providers returned for filter " - + filterString, expected, provTest.length); + Provider[] provTest = Security.getProviders(filterString); + int expected = allSupported.get(filterString); + assertEquals("Unexpected number of providers returned for filter " + filterString + + ":\n" + allSupported, + expected, provTest.length); } catch (InvalidParameterException e) { // NO OP } @@ -99,62 +88,43 @@ public class Security2Test extends junit.framework.TestCase { // exception try { - provTest = Security.getProviders("Signature.SHA1withDSA :512"); + Security.getProviders("Signature.SHA1withDSA :512"); fail("InvalidParameterException should be thrown <Signature.SHA1withDSA :512>"); } catch (InvalidParameterException e) { // Expected } } - /** - * @param key - * @return - */ private boolean isProviderData(String key) { return key.toUpperCase().startsWith("PROVIDER."); } - /** - * @param key - * @return - */ private boolean isAlias(String key) { return key.toUpperCase().startsWith("ALG.ALIAS."); } - /** - * @param table - * @param key - */ - private void addOrIncrementTable(Hashtable<String, Integer> table, String key) { + private void addOrIncrementTable(Map<String, Integer> table, String k) { + String key = k.toUpperCase(); if (table.containsKey(key)) { - Integer before = (Integer) table.get(key); - table.put(key, new Integer(before.intValue() + 1)); + int before = table.get(key); + table.put(key, before + 1); } else { - table.put(key, new Integer(1)); + table.put(key, 1); } } - /** - * @param filterMap - * @return - */ private int getProvidersCount(Map filterMap) { int result = 0; Provider[] allProviders = Security.getProviders(); // for each provider - for (int i = 0; i < allProviders.length; i++) { - Provider provider = allProviders[i]; + for (Provider provider : allProviders) { Set allProviderKeys = provider.keySet(); boolean noMatchFoundForFilterEntry = false; // for each filter item - Set allFilterKeys = filterMap.keySet(); - Iterator fkIter = allFilterKeys.iterator(); - while (fkIter.hasNext()) { - String filterString = ((String) fkIter.next()).trim(); - + for (Object filter : filterMap.keySet()) { + String filterString = (String) filter; // Remove any "=" characters that may be on the end of the // map keys (no, I don't know why they might be there either // but I have seen them) @@ -211,10 +181,10 @@ public class Security2Test extends junit.framework.TestCase { // Test for method void // java.security.Security.getProviders(java.util.Map) - Map<String, String> filter = new Hashtable<String, String>(); + Map<String, String> filter = new HashMap<String, String>(); filter.put("KeyStore.BKS", ""); filter.put("Signature.SHA1withDSA", ""); - Provider provTest[] = Security.getProviders(filter); + Provider[] provTest = Security.getProviders(filter); if (provTest == null) { assertEquals("Filter : <KeyStore.BKS>,<Signature.SHA1withDSA>", 0, getProvidersCount(filter)); @@ -223,7 +193,7 @@ public class Security2Test extends junit.framework.TestCase { getProvidersCount(filter), provTest.length); } - filter = new Hashtable<String, String>(); + filter = new HashMap<String, String>(); filter.put("MessageDigest.SHA-384", ""); filter.put("CertificateFactory.X.509", ""); filter.put("KeyFactory.RSA", ""); @@ -237,7 +207,7 @@ public class Security2Test extends junit.framework.TestCase { getProvidersCount(filter), provTest.length); } - filter = new Hashtable<String, String>(); + filter = new HashMap<String, String>(); filter.put("MessageDigest.SHA1", ""); filter.put("TrustManagerFactory.X509", ""); provTest = Security.getProviders(filter); @@ -250,7 +220,7 @@ public class Security2Test extends junit.framework.TestCase { getProvidersCount(filter), provTest.length); } - filter = new Hashtable<String, String>(); + filter = new HashMap<String, String>(); filter.put("CertificateFactory.X509", ""); provTest = Security.getProviders(filter); if (provTest == null) { @@ -261,7 +231,7 @@ public class Security2Test extends junit.framework.TestCase { getProvidersCount(filter), provTest.length); } - filter = new Hashtable<String, String>(); + filter = new HashMap<String, String>(); filter.put("Provider.id name", "DRLCertFactory"); provTest = Security.getProviders(filter); assertNull("Filter : <Provider.id name, DRLCertFactory >", @@ -270,7 +240,7 @@ public class Security2Test extends junit.framework.TestCase { // exception - no attribute name after the service.algorithm yet we // still supply an expected value. This is not valid. try { - filter = new Hashtable<String, String>(); + filter = new HashMap<String, String>(); filter.put("Signature.SHA1withDSA", "512"); provTest = Security.getProviders(filter); fail("InvalidParameterException should be thrown <Signature.SHA1withDSA><512>"); @@ -280,7 +250,7 @@ public class Security2Test extends junit.framework.TestCase { // exception - space character in the service.algorithm pair. Not valid. try { - filter = new Hashtable<String, String>(); + filter = new HashMap<String, String>(); filter.put("Signature. KeySize", "512"); provTest = Security.getProviders(filter); fail("InvalidParameterException should be thrown <Signature. KeySize><512>"); @@ -320,11 +290,9 @@ public class Security2Test extends junit.framework.TestCase { assertTrue("Failed to add provider", addResult != -1); Security.removeProvider(entrust.getName()); - Provider provTest[] = Security.getProviders(); - for (int i = 0; i < provTest.length; i++) { - assertTrue( - "the provider entrust is found after it was removed", - provTest[i].getName() != entrust.getName()); + for (Provider provider : Security.getProviders()) { + assertTrue("the provider entrust is found after it was removed", + provider.getName() != entrust.getName()); } } finally { // Tidy up - the following calls do nothing if the providers were diff --git a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/CertPinManagerTest.java b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/CertPinManagerTest.java new file mode 100644 index 0000000..8359c99 --- /dev/null +++ b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/CertPinManagerTest.java @@ -0,0 +1,175 @@ +/* + * 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.File; +import java.io.FileWriter; +import java.security.cert.X509Certificate; +import java.security.KeyStore; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import junit.framework.TestCase; +import libcore.java.security.TestKeyStore; + +public class CertPinManagerTest extends TestCase { + + private X509Certificate[] chain; + private List<X509Certificate> shortChain; + private List<X509Certificate> longChain; + private String shortPin; + private String longPin; + private List<File> tmpFiles = new ArrayList<File>(); + + private String writeTmpPinFile(String text) throws Exception { + File tmp = File.createTempFile("pins", null); + FileWriter fstream = new FileWriter(tmp); + fstream.write(text); + fstream.close(); + tmpFiles.add(tmp); + return tmp.getPath(); + } + + private static String getFingerprint(X509Certificate cert) throws NoSuchAlgorithmException { + MessageDigest dgst = MessageDigest.getInstance("SHA512"); + byte[] encoded = cert.getPublicKey().getEncoded(); + byte[] fingerprint = dgst.digest(encoded); + return IntegralToString.bytesToHexString(fingerprint, false); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + // build some valid chains + KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); + chain = (X509Certificate[]) pke.getCertificateChain(); + X509Certificate root = chain[2]; + X509Certificate server = chain[0]; + + // build the short and long chains + shortChain = new ArrayList<X509Certificate>(); + shortChain.add(root); + longChain = new ArrayList<X509Certificate>(); + longChain.add(server); + + // we'll use the root as the pin for the short entry and the server as the pin for the long + shortPin = getFingerprint(root); + longPin = getFingerprint(server); + } + + @Override + public void tearDown() throws Exception { + try { + for (File f : tmpFiles) { + f.delete(); + } + tmpFiles.clear(); + } finally { + super.tearDown(); + } + } + + public void testPinFileMaximumLookup() throws Exception { + + // write a pinfile with two entries, one longer than the other + String shortEntry = "*.google.com=true|" + shortPin; + String longEntry = "*.clients.google.com=true|" + longPin; + + // create the pinFile + String path = writeTmpPinFile(shortEntry + "\n" + longEntry); + CertPinManager pf = new CertPinManager(path, new TrustedCertificateStore()); + + // verify that the shorter chain doesn't work for a name matching the longer + assertTrue("short chain long uri failed", + pf.chainIsNotPinned("android.clients.google.com", shortChain)); + // verify that the longer chain doesn't work for a name matching the shorter + assertTrue("long chain short uri failed", + pf.chainIsNotPinned("android.google.com", longChain)); + // verify that the shorter chain works for the shorter domain + assertTrue("short chain short uri failed", + !pf.chainIsNotPinned("android.google.com", shortChain)); + // and the same for the longer + assertTrue("long chain long uri failed", + !pf.chainIsNotPinned("android.clients.google.com", longChain)); + } + + public void testPinEntryMalformedEntry() throws Exception { + // set up the pinEntry with a bogus entry + String entry = "*.google.com="; + try { + new PinListEntry(entry, new TrustedCertificateStore()); + fail("Accepted an empty pin list entry."); + } catch (PinEntryException expected) { + } + } + + public void testPinEntryNull() throws Exception { + // set up the pinEntry with a bogus entry + String entry = null; + try { + new PinListEntry(entry, new TrustedCertificateStore()); + fail("Accepted a basically wholly bogus entry."); + } catch (NullPointerException expected) { + } + } + + public void testPinEntryEmpty() throws Exception { + // set up the pinEntry with a bogus entry + try { + new PinListEntry("", new TrustedCertificateStore()); + fail("Accepted an empty entry."); + } catch (PinEntryException expected) { + } + } + + public void testPinEntryPinFailure() throws Exception { + // write a pinfile with two entries, one longer than the other + String shortEntry = "*.google.com=true|" + shortPin; + + // set up the pinEntry with a pinlist that doesn't match what we'll give it + PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); + assertTrue("Not enforcing!", e.getEnforcing()); + // verify that it doesn't accept + boolean retval = e.chainIsNotPinned(longChain); + assertTrue("Accepted an incorrect pinning, this is very bad", retval); + } + + public void testPinEntryPinSuccess() throws Exception { + // write a pinfile with two entries, one longer than the other + String shortEntry = "*.google.com=true|" + shortPin; + + // set up the pinEntry with a pinlist that matches what we'll give it + PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); + assertTrue("Not enforcing!", e.getEnforcing()); + // verify that it accepts + boolean retval = e.chainIsNotPinned(shortChain); + assertTrue("Failed on a correct pinning, this is very bad", !retval); + } + + public void testPinEntryNonEnforcing() throws Exception { + // write a pinfile with two entries, one longer than the other + String shortEntry = "*.google.com=false|" + shortPin; + + // set up the pinEntry with a pinlist that matches what we'll give it + PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); + assertFalse("Enforcing!", e.getEnforcing()); + // verify that it accepts + boolean retval = e.chainIsNotPinned(shortChain); + assertTrue("Failed on an unenforced pinning, this is bad-ish", !retval); + } +} 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 c8df4ab..303c234 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 @@ -21,10 +21,14 @@ import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStore.PrivateKeyEntry; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -38,6 +42,7 @@ import javax.net.ssl.SSLException; import javax.net.ssl.SSLProtocolException; import javax.security.auth.x500.X500Principal; import junit.framework.TestCase; +import libcore.io.IoUtils; import libcore.java.security.StandardNames; import libcore.java.security.TestKeyStore; import org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSLHandshakeCallbacks; @@ -47,7 +52,8 @@ public class NativeCryptoTest extends TestCase { private static final int NULL = 0; private static final FileDescriptor INVALID_FD = new FileDescriptor(); - private static final SSLHandshakeCallbacks DUMMY_CB = new TestSSLHandshakeCallbacks(-1, null); + private static final SSLHandshakeCallbacks DUMMY_CB + = new TestSSLHandshakeCallbacks(null, 0, null); private static final long TIMEOUT_SECONDS = 5; @@ -131,6 +137,87 @@ public class NativeCryptoTest extends TestCase { assertEquals(Arrays.deepToString(expected), Arrays.deepToString(actual)); } + public void test_EVP_PKEY_cmp() throws Exception { + try { + NativeCrypto.EVP_PKEY_cmp(NULL, NULL); + fail("Should throw NullPointerException when arguments are NULL"); + } catch (NullPointerException expected) { + } + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(512); + + KeyPair kp1 = kpg.generateKeyPair(); + RSAPrivateCrtKey privKey1 = (RSAPrivateCrtKey) kp1.getPrivate(); + + KeyPair kp2 = kpg.generateKeyPair(); + RSAPrivateCrtKey privKey2 = (RSAPrivateCrtKey) kp2.getPrivate(); + + int pkey1 = 0, pkey1_copy = 0, pkey2 = 0; + try { + pkey1 = NativeCrypto.EVP_PKEY_new_RSA(privKey1.getModulus().toByteArray(), + privKey1.getPublicExponent().toByteArray(), + privKey1.getPrivateExponent().toByteArray(), + privKey1.getPrimeP().toByteArray(), + privKey1.getPrimeQ().toByteArray(), + privKey1.getPrimeExponentP().toByteArray(), + privKey1.getPrimeExponentQ().toByteArray(), + privKey1.getCrtCoefficient().toByteArray()); + assertNotSame(NULL, pkey1); + + pkey1_copy = NativeCrypto.EVP_PKEY_new_RSA(privKey1.getModulus().toByteArray(), + privKey1.getPublicExponent().toByteArray(), + privKey1.getPrivateExponent().toByteArray(), + privKey1.getPrimeP().toByteArray(), + privKey1.getPrimeQ().toByteArray(), + privKey1.getPrimeExponentP().toByteArray(), + privKey1.getPrimeExponentQ().toByteArray(), + privKey1.getCrtCoefficient().toByteArray()); + assertNotSame(NULL, pkey1_copy); + + pkey2 = NativeCrypto.EVP_PKEY_new_RSA(privKey2.getModulus().toByteArray(), + privKey2.getPublicExponent().toByteArray(), + privKey2.getPrivateExponent().toByteArray(), + privKey2.getPrimeP().toByteArray(), + privKey2.getPrimeQ().toByteArray(), + privKey2.getPrimeExponentP().toByteArray(), + privKey2.getPrimeExponentQ().toByteArray(), + privKey2.getCrtCoefficient().toByteArray()); + assertNotSame(NULL, pkey2); + + try { + NativeCrypto.EVP_PKEY_cmp(pkey1, NULL); + fail("Should throw NullPointerException when arguments are NULL"); + } catch (NullPointerException expected) { + } + + try { + NativeCrypto.EVP_PKEY_cmp(NULL, pkey1); + fail("Should throw NullPointerException when arguments are NULL"); + } catch (NullPointerException expected) { + } + + assertEquals("Same keys should be the equal", 1, + NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1)); + + assertEquals("Same keys should be the equal", 1, + NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1_copy)); + + assertEquals("Different keys should not be equal", 0, + NativeCrypto.EVP_PKEY_cmp(pkey1, pkey2)); + } finally { + if (pkey1 != 0) { + NativeCrypto.EVP_PKEY_free(pkey1); + } + if (pkey1_copy != 0) { + NativeCrypto.EVP_PKEY_free(pkey1_copy); + } + if (pkey2 != 0) { + NativeCrypto.EVP_PKEY_free(pkey2); + } + } + } + public void test_SSL_CTX_new() throws Exception { int c = NativeCrypto.SSL_CTX_new(); assertTrue(c != NULL); @@ -490,11 +577,14 @@ public class NativeCryptoTest extends TestCase { } public static class TestSSLHandshakeCallbacks implements SSLHandshakeCallbacks { + private final Socket socket; private final int sslNativePointer; private final Hooks hooks; - public TestSSLHandshakeCallbacks(int sslNativePointer, + public TestSSLHandshakeCallbacks(Socket socket, + int sslNativePointer, Hooks hooks) { + this.socket = socket; this.sslNativePointer = sslNativePointer; this.hooks = hooks; } @@ -546,6 +636,10 @@ public class NativeCryptoTest extends TestCase { } this.handshakeCompletedCalled = true; } + + public Socket getSocket() { + return socket; + } } public static class ServerHooks extends Hooks { @@ -577,18 +671,19 @@ public class NativeCryptoTest extends TestCase { 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()) : listener.accept()); if (timeout == -1) { - return null; + return new TestSSLHandshakeCallbacks(socket, 0, null); } FileDescriptor fd = socket.getFileDescriptor$(); int c = hooks.getContext(); int s = hooks.beforeHandshake(c); - TestSSLHandshakeCallbacks callback = new TestSSLHandshakeCallbacks(s, hooks); + TestSSLHandshakeCallbacks callback + = new TestSSLHandshakeCallbacks(socket, s, hooks); if (DEBUG) { System.out.println("ssl=0x" + Integer.toString(s, 16) + " handshake" @@ -598,14 +693,19 @@ public class NativeCryptoTest extends TestCase { + " timeout=" + timeout + " client=" + client); } - int session = NativeCrypto.SSL_do_handshake(s, fd, callback, timeout, client, - npnProtocols); - if (DEBUG) { - System.out.println("ssl=0x" + Integer.toString(s, 16) - + " handshake" - + " session=0x" + Integer.toString(session, 16)); + int session = NULL; + try { + session = NativeCrypto.SSL_do_handshake(s, fd, callback, timeout, client, + npnProtocols); + if (DEBUG) { + System.out.println("ssl=0x" + Integer.toString(s, 16) + + " handshake" + + " session=0x" + Integer.toString(session, 16)); + } + } finally { + // Ensure afterHandshake is called to free resources + hooks.afterHandshake(session, s, c, socket, fd, callback); } - hooks.afterHandshake(session, s, c, socket, fd, callback); return callback; } }); @@ -777,17 +877,21 @@ public class NativeCryptoTest extends TestCase { Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { - NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER); - NativeCrypto.SSL_set_options( - s, NativeCrypto.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - NativeCrypto.SSL_renegotiate(s); - NativeCrypto.SSL_write(s, fd, callback, new byte[] { 42 }, 0, 1); - super.afterHandshake(session, s, c, sock, fd, callback); + try { + NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER); + NativeCrypto.SSL_set_options( + s, NativeCrypto.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + NativeCrypto.SSL_renegotiate(s); + NativeCrypto.SSL_write(s, fd, callback, new byte[] { 42 }, 0, 1, + (int) ((TIMEOUT_SECONDS * 1000) / 2)); + } catch (IOException expected) { + } finally { + super.afterHandshake(session, s, c, sock, fd, callback); + } } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null); - server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); try { client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } catch (ExecutionException e) { @@ -795,35 +899,49 @@ public class NativeCryptoTest extends TestCase { throw e; } } + server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_do_handshake_client_timeout() throws Exception { // client timeout final ServerSocket listener = new ServerSocket(0); + Socket serverSocket = null; try { Hooks cHooks = new Hooks(); Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 1, true, cHooks, null); Future<TestSSLHandshakeCallbacks> server = handshake(listener, -1, false, sHooks, null); + serverSocket = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket(); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); fail(); } catch (ExecutionException expected) { + if (SocketTimeoutException.class != expected.getCause().getClass()) { + expected.printStackTrace(); + } assertEquals(SocketTimeoutException.class, expected.getCause().getClass()); + } finally { + // Manually close peer socket when testing timeout + IoUtils.closeQuietly(serverSocket); } } public void test_SSL_do_handshake_server_timeout() throws Exception { // server timeout final ServerSocket listener = new ServerSocket(0); + Socket clientSocket = null; try { Hooks cHooks = new Hooks(); Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, -1, true, cHooks, null); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 1, false, sHooks, null); + clientSocket = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket(); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); fail(); } catch (ExecutionException expected) { assertEquals(SocketTimeoutException.class, expected.getCause().getClass()); + } finally { + // Manually close peer socket when testing timeout + IoUtils.closeQuietly(clientSocket); } } @@ -1150,7 +1268,7 @@ public class NativeCryptoTest extends TestCase { SSLHandshakeCallbacks callback) throws Exception { NativeCrypto.SSL_renegotiate(s); - NativeCrypto.SSL_write(s, fd, callback, new byte[] { 42 }, 0, 1); + NativeCrypto.SSL_write(s, fd, callback, new byte[] { 42 }, 0, 1, 0); super.afterHandshake(session, s, c, sock, fd, callback); } }; @@ -1317,7 +1435,7 @@ public class NativeCryptoTest extends TestCase { Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { - NativeCrypto.SSL_write(s, fd, callback, BYTES, 0, BYTES.length); + NativeCrypto.SSL_write(s, fd, callback, BYTES, 0, BYTES.length, 0); super.afterHandshake(session, s, c, sock, fd, callback); } }; @@ -1360,7 +1478,7 @@ public class NativeCryptoTest extends TestCase { public void test_SSL_write() throws Exception { try { - NativeCrypto.SSL_write(NULL, null, null, null, 0, 0); + NativeCrypto.SSL_write(NULL, null, null, null, 0, 0, 0); fail(); } catch (NullPointerException expected) { } @@ -1370,7 +1488,7 @@ public class NativeCryptoTest extends TestCase { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { - NativeCrypto.SSL_write(s, null, DUMMY_CB, null, 0, 1); + NativeCrypto.SSL_write(s, null, DUMMY_CB, null, 0, 1, 0); fail(); } catch (NullPointerException expected) { } @@ -1383,7 +1501,7 @@ public class NativeCryptoTest extends TestCase { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { - NativeCrypto.SSL_write(s, INVALID_FD, null, null, 0, 1); + NativeCrypto.SSL_write(s, INVALID_FD, null, null, 0, 1, 0); fail(); } catch (NullPointerException expected) { } @@ -1396,7 +1514,7 @@ public class NativeCryptoTest extends TestCase { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { - NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, null, 0, 1); + NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, null, 0, 1, 0); fail(); } catch (NullPointerException expected) { } @@ -1409,7 +1527,7 @@ public class NativeCryptoTest extends TestCase { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { - NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1); + NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0); fail(); } catch (SSLException expected) { } @@ -1628,76 +1746,6 @@ public class NativeCryptoTest extends TestCase { server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } - public void test_SSL_SESSION_compress_meth_null() throws Exception { - try { - NativeCrypto.SSL_SESSION_compress_meth(NULL, NULL); - fail(); - } catch (NullPointerException expected) { - } - - { - int c = NativeCrypto.SSL_CTX_new(); - try { - NativeCrypto.SSL_SESSION_compress_meth(c, NULL); - } catch (NullPointerException expected) { - } - NativeCrypto.SSL_CTX_free(c); - } - } - - public void test_SSL_SESSION_compress_meth_NULL() throws Exception { - final ServerSocket listener = new ServerSocket(0); - - Hooks cHooks = new Hooks() { - @Override - public void afterHandshake(int session, int s, int c, - Socket sock, FileDescriptor fd, - SSLHandshakeCallbacks callback) - throws Exception { - assertEquals("NULL", NativeCrypto.SSL_SESSION_compress_meth(c, session)); - super.afterHandshake(session, s, c, sock, fd, callback); - } - }; - Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); - Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null); - Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null); - client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); - server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); - } - - public void test_SSL_SESSION_compress_meth_ZLIB() throws Exception { - final ServerSocket listener = new ServerSocket(0); - - Hooks cHooks = new Hooks() { - @Override - public int beforeHandshake(int c) throws SSLException { - int s = super.beforeHandshake(c); - NativeCrypto.SSL_clear_options(s, NativeCrypto.SSL_OP_NO_COMPRESSION); - return s; - } - @Override - public void afterHandshake(int session, int s, int c, - Socket sock, FileDescriptor fd, - SSLHandshakeCallbacks callback) - throws Exception { - assertEquals("ZLIB", NativeCrypto.SSL_SESSION_compress_meth(c, session)); - super.afterHandshake(session, s, c, sock, fd, callback); - } - }; - Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { - @Override - public int beforeHandshake(int c) throws SSLException { - int s = super.beforeHandshake(c); - NativeCrypto.SSL_clear_options(s, NativeCrypto.SSL_OP_NO_COMPRESSION); - return s; - } - }; - Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null); - Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null); - client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); - server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); - } - public void test_SSL_SESSION_free() throws Exception { try { NativeCrypto.SSL_SESSION_free(NULL); @@ -1728,6 +1776,12 @@ public class NativeCryptoTest extends TestCase { 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); } @@ -1749,7 +1803,7 @@ public class NativeCryptoTest extends TestCase { 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() { @@ -1771,4 +1825,25 @@ public class NativeCryptoTest extends TestCase { // 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/TrustManagerImplTest.java b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImplTest.java index 26ebc85..fe5f4f0 100644 --- a/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImplTest.java +++ b/luni/src/test/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImplTest.java @@ -16,9 +16,15 @@ package org.apache.harmony.xnet.provider.jsse; -import java.security.KeyStore; +import java.io.File; +import java.io.FileWriter; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.security.KeyStore; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; @@ -27,12 +33,41 @@ import libcore.java.security.TestKeyStore; public class TrustManagerImplTest extends TestCase { + private List<File> tmpFiles = new ArrayList<File>(); + + private String getFingerprint(X509Certificate cert) throws Exception { + MessageDigest dgst = MessageDigest.getInstance("SHA512"); + byte[] encoded = cert.getPublicKey().getEncoded(); + byte[] fingerprint = dgst.digest(encoded); + return IntegralToString.bytesToHexString(fingerprint, false); + } + + private String writeTmpPinFile(String text) throws Exception { + File tmp = File.createTempFile("pins", null); + FileWriter fstream = new FileWriter(tmp); + fstream.write(text); + fstream.close(); + tmpFiles.add(tmp); + return tmp.getPath(); + } + + @Override + public void tearDown() throws Exception { + try { + for (File f : tmpFiles) { + f.delete(); + } + tmpFiles.clear(); + } finally { + super.tearDown(); + } + } + /** * Ensure that our non-standard behavior of learning to trust new * intermediate CAs does not regress. http://b/3404902 */ public void testLearnIntermediate() throws Exception { - // chain3 should be server/intermediate/root KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain(); @@ -63,6 +98,52 @@ public class TrustManagerImplTest extends TestCase { assertValid(chain1, tm); } + public void testGetFullChain() throws Exception { + // build the trust manager + KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); + X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain(); + X509Certificate root = chain3[2]; + X509TrustManager tm = trustManager(root); + + // build the chains we'll use for testing + X509Certificate intermediate = chain3[1]; + X509Certificate server = chain3[0]; + X509Certificate[] chain2 = new X509Certificate[] { server, intermediate }; + X509Certificate[] chain1 = new X509Certificate[] { server }; + + assertTrue(tm instanceof TrustManagerImpl); + TrustManagerImpl tmi = (TrustManagerImpl) tm; + List<X509Certificate> certs = tmi.checkServerTrusted(chain2, "RSA", "purple.com"); + assertEquals(Arrays.asList(chain3), certs); + certs = tmi.checkServerTrusted(chain1, "RSA", "purple.com"); + assertEquals(Arrays.asList(chain3), certs); + } + + public void testCertPinning() throws Exception { + // chain3 should be server/intermediate/root + KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); + X509Certificate[] chain3 = (X509Certificate[]) pke.getCertificateChain(); + X509Certificate root = chain3[2]; + X509Certificate intermediate = chain3[1]; + X509Certificate server = chain3[0]; + X509Certificate[] chain2 = new X509Certificate[] { server, intermediate }; + X509Certificate[] chain1 = new X509Certificate[] { server }; + + // test without a hostname, expecting failure + assertInvalidPinned(chain1, trustManager(root, "gugle.com", root), null); + // test without a hostname, expecting success + assertValidPinned(chain3, trustManager(root, "gugle.com", root), null, chain3); + // test an unpinned hostname that should fail + assertInvalidPinned(chain1, trustManager(root, "gugle.com", root), "purple.com"); + // test an unpinned hostname that should succeed + assertValidPinned(chain3, trustManager(root, "gugle.com", root), "purple.com", chain3); + // test a pinned hostname that should fail + assertInvalidPinned(chain1, trustManager(intermediate, "gugle.com", root), "gugle.com"); + // test a pinned hostname that should succeed + assertValidPinned(chain2, trustManager(intermediate, "gugle.com", server), "gugle.com", + chain2); + } + private X509TrustManager trustManager(X509Certificate ca) throws Exception { KeyStore keyStore = TestKeyStore.createKeyStore(); keyStore.setCertificateEntry("alias", ca); @@ -73,10 +154,45 @@ public class TrustManagerImplTest extends TestCase { return (X509TrustManager) tmf.getTrustManagers()[0]; } + private TrustManagerImpl trustManager(X509Certificate ca, String hostname, X509Certificate pin) + throws Exception { + // build the cert pin manager + CertPinManager cm = certManager(hostname, pin); + // insert it into the trust manager + KeyStore keyStore = TestKeyStore.createKeyStore(); + keyStore.setCertificateEntry("alias", ca); + return new TrustManagerImpl(keyStore, cm); + } + + private CertPinManager certManager(String hostname, X509Certificate pin) throws Exception { + String pinString = ""; + if (pin != null) { + pinString = hostname + "=true|" + getFingerprint(pin); + } + // write it to a pinfile + String path = writeTmpPinFile(pinString); + // build the certpinmanager + return new CertPinManager(path, new TrustedCertificateStore()); + } + private void assertValid(X509Certificate[] chain, X509TrustManager tm) throws Exception { - tm.checkClientTrusted(chain, "RSA"); + if (tm instanceof TrustManagerImpl) { + TrustManagerImpl tmi = (TrustManagerImpl) tm; + tmi.checkServerTrusted(chain, "RSA"); + } tm.checkServerTrusted(chain, "RSA"); } + + private void assertValidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname, + X509Certificate[] fullChain) throws Exception { + if (tm instanceof TrustManagerImpl) { + TrustManagerImpl tmi = (TrustManagerImpl) tm; + List<X509Certificate> checkedChain = tmi.checkServerTrusted(chain, "RSA", hostname); + assertEquals(checkedChain, Arrays.asList(fullChain)); + } + tm.checkServerTrusted(chain, "RSA"); + } + private void assertInvalid(X509Certificate[] chain, X509TrustManager tm) { try { tm.checkClientTrusted(chain, "RSA"); @@ -89,4 +205,15 @@ public class TrustManagerImplTest extends TestCase { } catch (CertificateException expected) { } } + + private void assertInvalidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname) + throws Exception { + assertTrue(tm.getClass().getName(), tm instanceof TrustManagerImpl); + try { + TrustManagerImpl tmi = (TrustManagerImpl) tm; + tmi.checkServerTrusted(chain, "RSA", hostname); + fail(); + } catch (CertificateException expected) { + } + } } 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..8f9b7fa 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.io.OutputStream; 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 @@ public class TrustedCertificateStoreTest extends TestCase { 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(); } @@ -519,6 +530,26 @@ public class TrustedCertificateStoreTest extends TestCase { assertDeleted(getCa1(), getAliasSystemCa1()); } + public void testIsUserAddedCertificate() throws Exception { + assertFalse(store.isUserAddedCertificate(getCa1())); + assertFalse(store.isUserAddedCertificate(getCa2())); + install(getCa1(), getAliasSystemCa1()); + assertFalse(store.isUserAddedCertificate(getCa1())); + assertFalse(store.isUserAddedCertificate(getCa2())); + install(getCa1(), getAliasUserCa1()); + assertTrue(store.isUserAddedCertificate(getCa1())); + assertFalse(store.isUserAddedCertificate(getCa2())); + install(getCa2(), getAliasUserCa2()); + assertTrue(store.isUserAddedCertificate(getCa1())); + assertTrue(store.isUserAddedCertificate(getCa2())); + store.deleteCertificateEntry(getAliasUserCa1()); + assertFalse(store.isUserAddedCertificate(getCa1())); + assertTrue(store.isUserAddedCertificate(getCa2())); + store.deleteCertificateEntry(getAliasUserCa2()); + assertFalse(store.isUserAddedCertificate(getCa1())); + assertFalse(store.isUserAddedCertificate(getCa2())); + } + private void assertRootCa(X509Certificate x, String alias) { assertIntermediateCa(x, alias); assertEquals(x, store.findIssuer(x)); diff --git a/luni/src/test/java/tests/api/java/lang/ref/PhantomReferenceTest.java b/luni/src/test/java/tests/api/java/lang/ref/PhantomReferenceTest.java index 06221c9..6470579 100644 --- a/luni/src/test/java/tests/api/java/lang/ref/PhantomReferenceTest.java +++ b/luni/src/test/java/tests/api/java/lang/ref/PhantomReferenceTest.java @@ -20,6 +20,7 @@ package tests.api.java.lang.ref; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; +import libcore.java.lang.ref.FinalizationTester; //TODO: write a test to verify that the referent's finalize() happens // before the PhantomReference is enqueued. @@ -81,8 +82,8 @@ public class PhantomReferenceTest extends junit.framework.TestCase { Thread t = new TestThread(); t.start(); t.join(); - System.gc(); - System.runFinalization(); + + FinalizationTester.induceFinalization(); assertNull("get() should return null.", tprs[0].get()); assertNull("get() should return null.", tprs[1].get()); diff --git a/luni/src/test/java/tests/api/java/lang/ref/ReferenceQueueTest.java b/luni/src/test/java/tests/api/java/lang/ref/ReferenceQueueTest.java index dc7e738..cad61b3 100644 --- a/luni/src/test/java/tests/api/java/lang/ref/ReferenceQueueTest.java +++ b/luni/src/test/java/tests/api/java/lang/ref/ReferenceQueueTest.java @@ -22,6 +22,7 @@ import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; +import libcore.java.lang.ref.FinalizationTester; public class ReferenceQueueTest extends junit.framework.TestCase { static Boolean b; @@ -97,8 +98,7 @@ public class ReferenceQueueTest extends junit.framework.TestCase { sr.enqueue(); wr.enqueue(); - System.gc(); - System.runFinalization(); + FinalizationTester.induceFinalization(); assertNull(rq.poll()); } diff --git a/luni/src/test/java/tests/api/java/lang/ref/ReferenceTest.java b/luni/src/test/java/tests/api/java/lang/ref/ReferenceTest.java index a1a7a8c..7461b47 100644 --- a/luni/src/test/java/tests/api/java/lang/ref/ReferenceTest.java +++ b/luni/src/test/java/tests/api/java/lang/ref/ReferenceTest.java @@ -22,6 +22,7 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import junit.framework.AssertionFailedError; +import libcore.java.lang.ref.FinalizationTester; public class ReferenceTest extends junit.framework.TestCase { Object tmpA, tmpB, tmpC, obj; @@ -146,16 +147,14 @@ public class ReferenceTest extends junit.framework.TestCase { ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); r = newWeakReference(queue); - System.gc(); - System.runFinalization(); + FinalizationTester.induceFinalization(); Reference ref = queue.remove(); assertNotNull("Object not enqueued.", ref); assertSame("Unexpected ref1", ref, r); assertNull("Object could not be reclaimed1.", r.get()); r = newWeakReference(queue); - System.gc(); - System.runFinalization(); + FinalizationTester.induceFinalization(); // wait for the reference queue thread to enqueue the newly-finalized object Thread.yield(); @@ -213,8 +212,7 @@ public class ReferenceTest extends junit.framework.TestCase { Thread t = new TestThread(); t.start(); t.join(); - System.gc(); - System.runFinalization(); + FinalizationTester.induceFinalization(); ref = rq.remove(5000L); // Give up after five seconds. assertNotNull("Object not garbage collected.", ref); @@ -238,8 +236,7 @@ public class ReferenceTest extends junit.framework.TestCase { public void test_get() { WeakReference ref = newWeakReference(null); - System.gc(); - System.runFinalization(); + FinalizationTester.induceFinalization(); assertNull("get() doesn't return null after gc for WeakReference", ref.get()); obj = new Object(); @@ -322,8 +319,7 @@ public class ReferenceTest extends junit.framework.TestCase { Thread t = new TestThread(); t.start(); t.join(); - System.gc(); - System.runFinalization(); + FinalizationTester.induceFinalization(); Thread.sleep(1000); if (error != null) { throw error; diff --git a/luni/src/test/java/tests/api/java/lang/ref/SoftReferenceTest.java b/luni/src/test/java/tests/api/java/lang/ref/SoftReferenceTest.java index 77c6536..197d829 100644 --- a/luni/src/test/java/tests/api/java/lang/ref/SoftReferenceTest.java +++ b/luni/src/test/java/tests/api/java/lang/ref/SoftReferenceTest.java @@ -22,6 +22,7 @@ import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.Vector; +import libcore.java.lang.ref.FinalizationTester; public class SoftReferenceTest extends junit.framework.TestCase { static Boolean bool; @@ -124,8 +125,7 @@ public class SoftReferenceTest extends junit.framework.TestCase { TestThread t = new TestThread(); t.start(); t.join(); - System.gc(); - System.runFinalization(); + FinalizationTester.induceFinalization(); ref = rq.poll(); assertNotNull("Object not garbage collected.", ref); assertNull("Object is not null.", ref.get()); 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 @@ public class SimpleTimeZoneTest extends junit.framework.TestCase { * 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/java/util/WeakHashMapTest.java b/luni/src/test/java/tests/api/java/util/WeakHashMapTest.java index 0e43bf6..d1a43e5 100644 --- a/luni/src/test/java/tests/api/java/util/WeakHashMapTest.java +++ b/luni/src/test/java/tests/api/java/util/WeakHashMapTest.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import libcore.java.lang.ref.FinalizationTester; import tests.support.Support_MapTest2; @@ -208,7 +209,7 @@ public class WeakHashMapTest extends junit.framework.TestCase { do { System.gc(); System.gc(); - Runtime.getRuntime().runFinalization(); + FinalizationTester.induceFinalization(); count++; } while (count <= 5 && entrySet.size() == 100); @@ -240,7 +241,8 @@ public class WeakHashMapTest extends junit.framework.TestCase { WeakHashMap map = new WeakHashMap(); map.put(null, "value"); // add null key System.gc(); - System.runFinalization(); + System.gc(); + FinalizationTester.induceFinalization(); map.remove("nothing"); // Cause objects in queue to be removed assertEquals("null key was removed", 1, map.size()); } @@ -315,7 +317,7 @@ public class WeakHashMapTest extends junit.framework.TestCase { do { System.gc(); System.gc(); - Runtime.getRuntime().runFinalization(); + FinalizationTester.induceFinalization(); count++; } while (count <= 5 && keySet.size() == 100); @@ -352,7 +354,7 @@ public class WeakHashMapTest extends junit.framework.TestCase { do { System.gc(); System.gc(); - Runtime.getRuntime().runFinalization(); + FinalizationTester.induceFinalization(); count++; } while (count <= 5 && valuesCollection.size() == 100); 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 javax.net.ServerSocketFactory; 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(); - - try { - ServerSocket ss = sf.createServerSocket(portNumber); - assertNotNull(ss); - } catch (Exception ex) { - fail("Unexpected exception: " + ex); - } + ServerSocket ss = sf.createServerSocket(0); + assertNotNull(ss); 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(); - - try { - ServerSocket ss = sf.createServerSocket(portNumber, 0); - assertNotNull(ss); - } catch (Exception ex) { - fail("Unexpected exception: " + ex); - } + ServerSocket ss = sf.createServerSocket(0, 0); + assertNotNull(ss); 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(); - try { - ServerSocket ss = sf.createServerSocket(portNumber, 0, InetAddress.getLocalHost()); - assertNotNull(ss); - } catch (Exception ex) { - fail("Unexpected exception: " + ex); - } + ServerSocket ss = sf.createServerSocket(0, 0, InetAddress.getLocalHost()); + assertNotNull(ss); 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 javax.net.SocketFactory; 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 @@ public class SocketFactoryTest extends TestCase { * 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 @@ public class SocketFactoryTest extends TestCase { 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 @@ public class SocketFactoryTest extends TestCase { } 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 javax.security.cert.X509Certificate; 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 @@ import tests.support.Support_PortManager; */ 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 @@ public class HandshakeCompletedEventTest extends TestCase { // 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 @@ public class HandshakeCompletedEventTest extends TestCase { 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(); - } - public void run() { - try { - KeyManager[] keyManagers = provideKeys ? getKeyManagers(keys) : null; - TrustManager[] trustManagers = new TrustManager[] { trustManager }; + KeyManager[] keyManagers = provideKeys ? getKeyManagers(keys) : null; + TrustManager[] trustManagers = new TrustManager[] { trustManager }; - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagers, trustManagers, null); + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, null); - SSLServerSocket serverSocket = (SSLServerSocket) - sslContext.getServerSocketFactory().createServerSocket(); + 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); - } + 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)); + serverSocket.bind(new InetSocketAddress(0)); + } + public void run() { + try { SSLSocket clientSocket = (SSLSocket)serverSocket.accept(); InputStream istream = clientSocket.getInputStream(); @@ -497,7 +494,7 @@ public class HandshakeCompletedEventTest extends TestCase { 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 junit.framework.TestCase; 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 @@ public class SSLServerSocketTest extends TestCase { /** * 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(); - int[] port_invalid = {-1, 65536, Integer.MIN_VALUE, Integer.MAX_VALUE}; + public void testConstructor_IIInetAddress() throws Exception { + // A null InetAddress is okay. + new mySSLServerSocket(0, 0, null); - try { - ssl = new mySSLServerSocket(portNumber, 0, ia); - assertEquals(portNumber, ssl.getLocalPort()); - } catch (Exception ex) { - fail("Unexpected exception was thrown"); - } + int[] port_invalid = {-1, 65536, Integer.MIN_VALUE, Integer.MAX_VALUE}; - 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 junit.framework.TestCase; 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 class SSLSessionTest extends TestCase { 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 @@ public class SSLSessionTest extends TestCase { 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 @@ public class SSLSessionTest extends TestCase { + "NMGpCX6qmjbkJQLVK/Yfo1ePaUexPSOX0G9m8+DoV3iyNw6at01NRw=="; - int port; - SSLSocket serverSocket; + SSLServerSocket serverSocket; MyHandshakeListener listener; String host = "localhost"; boolean notFinished = true; @@ -489,36 +481,35 @@ public class SSLSessionTest extends TestCase { 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(); - } - public void run() { - try { - store = provideKeys ? getKeyStore(keys) : null; - KeyManager[] keyManagers = store != null ? getKeyManagers(store) : null; - TrustManager[] trustManagers = new TrustManager[] { trustManager }; + 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); + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, null); - SSLServerSocket serverSocket = (SSLServerSocket)sslContext - .getServerSocketFactory().createServerSocket(); + 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); - } + 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)); + serverSocket.bind(null); + } + public void run() { + try { SSLSocket clientSocket = (SSLSocket)serverSocket.accept(); InputStream istream = clientSocket.getInputStream(); @@ -589,7 +580,7 @@ public class SSLSessionTest extends TestCase { 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 javax.net.ssl.SSLSocketFactory; 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 javax.security.cert.X509Certificate; 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 @@ public class SSLSocketTest extends TestCase { /** * 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 @@ public class SSLSocketTest extends TestCase { /** * 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 @@ public class SSLSocketTest extends TestCase { * 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 @@ public class SSLSocketTest extends TestCase { /** * 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 @@ public class SSLSocketTest extends TestCase { * 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 @@ public class SSLSocketTest extends TestCase { } } - 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 @@ public class SSLSocketTest extends TestCase { 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 @@ public class SSLSocketTest extends TestCase { 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..19c6229 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 @@ -17,7 +17,8 @@ package tests.api.org.apache.harmony.kernel.dalvik; import java.lang.reflect.Field; - +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeUnit; import junit.framework.Assert; import junit.framework.TestCase; import sun.misc.Unsafe; @@ -27,8 +28,6 @@ import sun.misc.Unsafe; */ public class ThreadsTest extends TestCase { private static Unsafe UNSAFE = null; - private static RuntimeException INITIALIZEFAILED = null; - static { /* * Set up {@link #UNSAFE}. This subverts the access check to @@ -42,78 +41,94 @@ public class ThreadsTest extends TestCase { UNSAFE = (Unsafe) field.get(null); } catch (NoSuchFieldException ex) { - INITIALIZEFAILED = new RuntimeException(ex); + throw new RuntimeException(ex); } catch (IllegalAccessException ex) { - INITIALIZEFAILED = new RuntimeException(ex); + throw new RuntimeException(ex); } } /** Test the case where the park times out. */ - public void test_parkFor_1() { - Parker parker = new Parker(false, 500); + public void test_parkFor_1() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(2); + Parker parker = new Parker(barrier, false, 500); Thread parkerThread = new Thread(parker); Thread waiterThread = - new Thread(new WaitAndUnpark(1000, parkerThread)); + new Thread(new WaitAndUnpark(barrier, 1000, parkerThread)); parkerThread.start(); waiterThread.start(); parker.assertDurationIsInRange(500); + waiterThread.join(); + parkerThread.join(); } /** Test the case where the unpark happens before the timeout. */ - public void test_parkFor_2() { - Parker parker = new Parker(false, 1000); + public void test_parkFor_2() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(2); + Parker parker = new Parker(barrier, false, 1000); Thread parkerThread = new Thread(parker); Thread waiterThread = - new Thread(new WaitAndUnpark(300, parkerThread)); + new Thread(new WaitAndUnpark(barrier, 300, parkerThread)); parkerThread.start(); waiterThread.start(); parker.assertDurationIsInRange(300); + waiterThread.join(); + parkerThread.join(); } /** Test the case where the thread is preemptively unparked. */ - public void test_parkFor_3() { - Parker parker = new Parker(false, 1000); + public void test_parkFor_3() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(1); + Parker parker = new Parker(barrier, false, 1000); Thread parkerThread = new Thread(parker); UNSAFE.unpark(parkerThread); parkerThread.start(); parker.assertDurationIsInRange(0); + parkerThread.join(); } /** Test the case where the park times out. */ - public void test_parkUntil_1() { - Parker parker = new Parker(true, 500); + public void test_parkUntil_1() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(2); + Parker parker = new Parker(barrier, true, 500); Thread parkerThread = new Thread(parker); Thread waiterThread = - new Thread(new WaitAndUnpark(1000, parkerThread)); + new Thread(new WaitAndUnpark(barrier, 1000, parkerThread)); parkerThread.start(); waiterThread.start(); parker.assertDurationIsInRange(500); + waiterThread.join(); + parkerThread.join(); } /** Test the case where the unpark happens before the timeout. */ - public void test_parkUntil_2() { - Parker parker = new Parker(true, 1000); + public void test_parkUntil_2() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(2); + Parker parker = new Parker(barrier, true, 1000); Thread parkerThread = new Thread(parker); Thread waiterThread = - new Thread(new WaitAndUnpark(300, parkerThread)); + new Thread(new WaitAndUnpark(barrier, 300, parkerThread)); parkerThread.start(); waiterThread.start(); parker.assertDurationIsInRange(300); + waiterThread.join(); + parkerThread.join(); } /** Test the case where the thread is preemptively unparked. */ - public void test_parkUntil_3() { - Parker parker = new Parker(true, 1000); + public void test_parkUntil_3() throws Exception { + CyclicBarrier barrier = new CyclicBarrier(1); + Parker parker = new Parker(barrier, true, 1000); Thread parkerThread = new Thread(parker); UNSAFE.unpark(parkerThread); parkerThread.start(); parker.assertDurationIsInRange(0); + parkerThread.join(); } // TODO: Add more tests. @@ -123,6 +138,9 @@ public class ThreadsTest extends TestCase { * the indicated value, noting the duration of time actually parked. */ private static class Parker implements Runnable { + + private final CyclicBarrier barrier; + /** whether {@link #amount} is milliseconds to wait in an * absolute fashion (<code>true</code>) or nanoseconds to wait * in a relative fashion (<code>false</code>) */ @@ -147,7 +165,8 @@ public class ThreadsTest extends TestCase { * either case, this constructor takes a duration to park for * @param parkMillis the number of milliseconds to be parked */ - public Parker(boolean absolute, long parkMillis) { + public Parker(CyclicBarrier barrier, boolean absolute, long parkMillis) { + this.barrier = barrier; this.absolute = absolute; // Multiply by 1000000 because parkFor() takes nanoseconds. @@ -155,8 +174,14 @@ public class ThreadsTest extends TestCase { } public void run() { + try { + barrier.await(60, TimeUnit.SECONDS); + } catch (Exception e) { + throw new AssertionError(e); + } boolean absolute = this.absolute; long amount = this.amount; + long startNanos = System.nanoTime(); long start = System.currentTimeMillis(); if (absolute) { @@ -165,11 +190,11 @@ public class ThreadsTest extends TestCase { UNSAFE.park(false, amount); } - long end = System.currentTimeMillis(); + long endNanos = System.nanoTime(); synchronized (this) { - startMillis = start; - endMillis = end; + startMillis = startNanos / 1000000; + endMillis = endNanos / 1000000; completed = true; notifyAll(); } @@ -187,11 +212,10 @@ public class ThreadsTest extends TestCase { 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"); } } @@ -200,7 +224,7 @@ public class ThreadsTest extends TestCase { } /** - * Asserts that the actual duration is within 5% of the + * Asserts that the actual duration is within 10% of the * given expected time. * * @param expectedMillis the expected duration, in milliseconds @@ -210,18 +234,20 @@ public class ThreadsTest extends TestCase { * Allow a bit more slop for the maximum on "expected * instantaneous" results. */ - long minimum = (long) ((double) expectedMillis * 0.95); + long minimum = (long) ((double) expectedMillis * 0.90); long maximum = - Math.max((long) ((double) expectedMillis * 1.05), 10); + Math.max((long) ((double) expectedMillis * 1.10), 10); long waitMillis = Math.max(expectedMillis * 10, 10); long duration = getDurationMillis(waitMillis); if (duration < minimum) { Assert.fail("expected duration: " + expectedMillis + - "; actual too short: " + duration); + " minimum duration: " + minimum + + " actual duration too short: " + duration); } else if (duration > maximum) { Assert.fail("expected duration: " + expectedMillis + - "; actual too long: " + duration); + " maximum duration: " + maximum + + " actual duration too long: " + duration); } } } @@ -231,16 +257,23 @@ public class ThreadsTest extends TestCase { * specified amount of time and then unparks an indicated thread. */ private static class WaitAndUnpark implements Runnable { + private final CyclicBarrier barrier; private final long waitMillis; private final Thread thread; - public WaitAndUnpark(long waitMillis, Thread thread) { + public WaitAndUnpark(CyclicBarrier barrier, long waitMillis, Thread thread) { + this.barrier = barrier; this.waitMillis = waitMillis; this.thread = thread; } public void run() { try { + barrier.await(60, TimeUnit.SECONDS); + } catch (Exception e) { + throw new AssertionError(e); + } + try { Thread.sleep(waitMillis); } catch (InterruptedException ex) { throw new RuntimeException("shouldn't happen", ex); @@ -249,11 +282,4 @@ public class ThreadsTest extends TestCase { UNSAFE.unpark(thread); } } - - @Override - protected void setUp() throws Exception { - if (INITIALIZEFAILED != null) { - throw INITIALIZEFAILED; - } - } } diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java index e8b29e4..be880d3 100644 --- a/support/src/test/java/libcore/java/security/StandardNames.java +++ b/support/src/test/java/libcore/java/security/StandardNames.java @@ -85,6 +85,13 @@ public final class StandardNames extends Assert { */ public static final Map<String,Set<String>> PROVIDER_ALGORITHMS = new HashMap<String,Set<String>>(); + + public static final Map<String,Set<String>> CIPHER_MODES + = new HashMap<String,Set<String>>(); + + public static final Map<String,Set<String>> CIPHER_PADDINGS + = new HashMap<String,Set<String>>(); + private static void provide(String type, String algorithm) { Set<String> algorithms = PROVIDER_ALGORITHMS.get(type); if (algorithms == null) { @@ -102,6 +109,22 @@ public final class StandardNames extends Assert { assertNotNull(PROVIDER_ALGORITHMS.remove(type)); } } + private static void provideCipherModes(String algorithm, String newModes[]) { + Set<String> modes = CIPHER_MODES.get(algorithm); + if (modes == null) { + modes = new HashSet<String>(); + CIPHER_MODES.put(algorithm, modes); + } + modes.addAll(Arrays.asList(newModes)); + } + private static void provideCipherPaddings(String algorithm, String newPaddings[]) { + Set<String> paddings = CIPHER_MODES.get(algorithm); + if (paddings == null) { + paddings = new HashSet<String>(); + CIPHER_MODES.put(algorithm, paddings); + } + paddings.addAll(Arrays.asList(newPaddings)); + } static { provide("AlgorithmParameterGenerator", "DSA"); provide("AlgorithmParameterGenerator", "DiffieHellman"); @@ -122,7 +145,10 @@ public final class StandardNames extends Assert { provide("CertStore", "Collection"); provide("CertStore", "LDAP"); provide("CertificateFactory", "X.509"); + // TODO: provideCipherModes and provideCipherPaddings for other Ciphers provide("Cipher", "AES"); + provideCipherModes("AES", new String[] { "CBC", "CFB", "CTR", "CTS", "ECB", "OFB" }); + provideCipherPaddings("AES", new String[] { "NoPadding", "PKCS5Padding" }); provide("Cipher", "AESWrap"); provide("Cipher", "ARCFOUR"); provide("Cipher", "Blowfish"); @@ -274,6 +300,12 @@ public final class StandardNames extends Assert { 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) { @@ -316,17 +348,10 @@ public final class StandardNames extends Assert { unprovide("MessageDigest", "SHA"); provide("MessageDigest", "SHA-1"); - // different names: added "Encryption" suffix - unprovide("Signature", "MD5withRSA"); - provide("Signature", "MD5WithRSAEncryption"); - unprovide("Signature", "SHA1withRSA"); - provide("Signature", "SHA1WithRSAEncryption"); - unprovide("Signature", "SHA256WithRSA"); - provide("Signature", "SHA256WithRSAEncryption"); - unprovide("Signature", "SHA384WithRSA"); - provide("Signature", "SHA384WithRSAEncryption"); - 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"); @@ -440,6 +465,12 @@ public final class StandardNames extends Assert { // Android's CA store provide("KeyStore", "AndroidCAStore"); + + // Android's KeyStore provider + if (Security.getProvider("AndroidKeyStoreProvider") != null) { + provide("KeyStore", "AndroidKeyStore"); + provide("KeyPairGenerator", "AndroidKeyPairGenerator"); + } } } @@ -839,4 +870,18 @@ public final class StandardNames extends Assert { assertValidCipherSuites(CIPHER_SUITES, cipherSuites); assertEquals(CIPHER_SUITES_DEFAULT, Arrays.asList(cipherSuites)); } + + /** + * Get all supported mode names for the given cipher. + */ + public static Set<String> getModesForCipher(String cipher) { + return CIPHER_MODES.get(cipher); + } + + /** + * Get all supported padding names for the given cipher. + */ + public static Set<String> getPaddingsForCipher(String cipher) { + return CIPHER_PADDINGS.get(cipher); + } } diff --git a/support/src/test/java/tests/net/StuckServer.java b/support/src/test/java/tests/net/StuckServer.java index eababce..f7a3118 100644 --- a/support/src/test/java/tests/net/StuckServer.java +++ b/support/src/test/java/tests/net/StuckServer.java @@ -17,6 +17,7 @@ package tests.net; import java.io.IOException; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; @@ -26,48 +27,57 @@ import java.util.ArrayList; * A test ServerSocket that you can't connect to --- connects will time out. */ public final class StuckServer { + private static final boolean DEBUG = false; + private ServerSocket serverSocket; + private InetSocketAddress address; private ArrayList<Socket> clients = new ArrayList<Socket>(); - public StuckServer() throws IOException { + public StuckServer(boolean useBacklog) throws IOException { // Set a backlog and use it up so that we can expect the - // connection to time out. According to Steven's + // connection to time out. According to Stevens // 4.5 "listen function", Linux adds 3 to the specified // backlog, so we need to connect 4 times before it will hang. - serverSocket = new ServerSocket(0, 1); - for (int i = 0; i < 4; i++) { - clients.add(new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort())); - } - } - - public void unblockAfterMs(final int ms) { - Thread t = new Thread(new Runnable() { - @Override public void run() { - try { - Thread.sleep(ms); - for (Socket client : clients) { - client.close(); - } - clients.clear(); - clients.add(serverSocket.accept()); - } catch (Exception ex) { - ex.printStackTrace(); + // The trouble with this is that it won't hang forever. + // After 10s or so, the kernel allows a couple more connections. + // This mode is ony useful if you actually want to continue eventually; we use it to + // test non-blocking connects, for example, where you want to test every part of the code. + if (useBacklog) { + this.serverSocket = new ServerSocket(0, 1); + this.address = (InetSocketAddress) serverSocket.getLocalSocketAddress(); + if (DEBUG) { + System.err.println("StuckServer: " + serverSocket); + } + for (int i = 0; i < 4; ++i) { + Socket client = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); + clients.add(client); + if (DEBUG) { + System.err.println("StuckServer client " + i + " - " + client); } } - }); - t.start(); + } else { + // In general, though, you don't want to rely on listen(2) backlog. http://b/6971145. + // RFC 5737 implies this network will be unreachable. (There are two other networks + // to try if we have trouble with this one.) + // We've had trouble with 10.* in the past (because test labs running CTS often use + // net 10!) but hopefully this network will be better. + InetAddress testNet1 = InetAddress.getByAddress(new byte[] { (byte) 192, 0, 2, 0 }); + this.address = new InetSocketAddress(testNet1, 80); + } } public InetSocketAddress getLocalSocketAddress() { - return (InetSocketAddress) serverSocket.getLocalSocketAddress(); + return address; } public int getLocalPort() { - return serverSocket.getLocalPort(); + return address.getPort(); } public void close() throws IOException { - serverSocket.close(); + if (serverSocket != null) { + serverSocket.close(); + } for (Socket client : clients) { client.close(); } 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 @@ public class Support_TestWebServer implements Support_HttpConstants { } /** - * 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 @@ public class Support_TestWebServer implements Support_HttpConstants { * @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 |